1 Introduction

Multilayer Perceptrons (MLPs), often referred to as deep learning models or multilayer neural networks, are a powerful class of algorithms with wide applications. These networks consist of interconnected layers of artificial “neurons,” each processing information and passing it forward to subsequent layers. The first layer receives raw input data, while hidden layers transform this data through weighted connections and nonlinear functions, allowing the network to learn complex patterns. The final layer produces predictions or classifications.

In statistical modeling, multilayer neural networks can handle intricate relationships in data. Unlike linear regression or simple decision trees, multilayer neural network models can model highly nonlinear interactions and adapt to large-scale, high-dimensional datasets where classic statistical models often struggle. Their flexibility comes from their ability to iteratively adjust weights during training, refining their predictions as they encounter more data.

Despite their strengths, multilayer neural networks require careful tuning and large datasets to perform well.

  • Overfitting—where a model memorizes training data but fails on new inputs—is a common challenge, mitigated by techniques like dropout and regularization.

  • Their black box nature can also make interpreting results difficult, posing challenges in fields where explainability is crucial, such as healthcare or finance.

MLPs are a foundational architecture in modern machine learning and artificial intelligence. Their ability to approximate complex, nonlinear functions makes them highly versatile for a wide range of real-world problems.

There are various adaptations and extensions of MLPs, each developed for specific purposes across different fields. A solid understanding of MLPs is essential for grasping the underlying principles of many specialized neural network algorithms.


2 Architecture of MLP

A Multilayer Perceptron (MLP) is generalized from the perceptron by adding one or more hidden layers between the input and output layers. That is, an MLP has three major components:

  • An input layer
  • One or more hidden layers
  • An output layer

Each layer consists of multiple neurons (nodes) connected via weights, with nonlinear activation functions that capture complex relationships between features and the target, thereby improving the algorithm’s performance.

If an MLP has only one hidden layer, this MLP is usually called shallow neural network. Deep neural networks have two or more hidden layers.

In the subsequent sections, we will briefly introduce the theory of neural networks with an emphasis on shallow networks. Some case studies will also be included to demonstrate the application of neural network models in regression and classification.

2.1 Mathematics of MLP

For illustration, we use the following one-hidden-layer MLP regression to explain the mathematical foundations of the algorithm. The objective is to predict the target variable \(y\) using input features \(\{x_1, x_2, \cdots, x_n \}\). If activation functions \(\phi_1(\cdot)\) and \(\phi(\cdot)\) are correctly specified, then the target can be expressed as a composition of weights through function composition.

In essence, MLP is a problem of approximating a multi-variable function. Assume that there are \(n\) features in the scenario of the above one-hidden-layer MLP, the relationship between the target variable \(y\) is characterized by \(y = f(x_1, x_2, \cdots, x_n: \mathbf{w})\), where \(f(\cdot)\) is unknown and \(\mathbf{w}\) is the vector of unknown parameters. That is, in the above MLP, the target can be expressed as

\[ y = f(x_1, x_2, \cdots, x_n: \mathbf{w}) = \phi_2[\phi_1(x_1, x_2, \cdots, x_n: \mathbf{w})]. \]

Note that \(f(\cdot)\) is an arbitrary function \(f: \mathbb{R}^n \rightarrow \mathbb{R}\). For a given data set \(\{(x_{1i}, x_{2i}, \cdots, x_{ni})\}_{i=1}^n\) and estimated \(\hat{\mathbf{w}}\), depending on applications, the loss function is defined respectively by

  • Continuous Regression (MSE)

\[ \mathcal{L} = \frac{1}{N} \sum_{c=1}^C (y_i - \hat{y}_i)^2 \]

  • Discrete Classification (Cross-entropy)

\[ \mathcal{L} = - \sum_{c=1}^C y_c \log(\hat{y}_c) \] for \(C\) classes. Using the gradient decent method, we can express the weight updating formula in the following

\[ \mathbf{W}^{(j+1)} \leftarrow \mathbf{W}^{(j)} - \eta \frac{\partial \mathcal{L}}{\partial \mathbf{W}}\Big|_{\mathbf{W}=\mathbf{W}^{(j)}} \] where \(\eta\) is the hyperparameter of learning rate.

2.2 Universal Approximation Theorem

The Universal Approximation Theorem is a fundamental result in the theory of artificial neural networks, stating that a feedforward neural network with a single hidden layer containing a finite number of neurons can approximate continuous functions on compact subsets of \(\mathbb{R}^n\) under mild conditions on the activation function. The formal statement is given in the following:

Cybenko’s Theorem (1989): Let \(\phi(\cdot)\) be a continuous, non-constant activation function (e.g., sigmoid). Given any continuous function \(f: [0,1]^n \rightarrow \mathbb{R}\) and \(\epsilon > 0\), there exists a single-hidden-layer MLP (multilayer perceptron) with finitely many hidden nodes (neurons), denoted by \(N(\mathbf{x})\), such that

\[ \sup_{\mathbf{x}\in [0,1]^n} \big|f(\mathbf{x}) - N(\mathbf{x}) \big| < \epsilon. \]

The above analysis assumes the support of the multivariable function is \([0,1]^n\). This can be easily generalized to any function with general finite support \([a-1, b_1]\times [a_2, b_2] \times \cdots\times[a_k, b_k]\) via a linear transformation. For example, assume \(f_k(x_k)\) has support \([a_k, b_k]\), we can use linear transformation \(y_k = (x_k - a_k)/(b_k - a_k)\) which implies that \(y_k \in [0,1]\).

This explains why a one-hidden-layer perceptron can approximate any continuous function defined on a finite support using finitely many nodes in the hidden layer. For functions with infinite support, we can first approximate them on a finite domain and then apply the universal approximation theorem to achieve the desired result.

Recall that, the objective of regression analysis is to estimate the unknown regression function based on a sample taken from the underlying population and then use the estimated regression function to

  • assess the relationship between feature variables and the target variable;
  • predict the value of the target variable based on the values of the input feature variables.

The universal approximation theorem for shallow networks guarantees their capability as a function approximator, making it a natural theoretical foundation for neural network regression methods.

2.3 Some Graphical Demonstrations

This subsection uses a simulation approach to demonstrate why MLP is also a non-classical family of algorithmic regression models. We take a set of points on the surface of a hypothesized elliptic paraboloid regression surface and then use MLP to estimate the hypothetical elliptic paraboloid model. The elliptic paraboloid used to generate data is given by

\[ z = x_1^2 + x_2^2 \] The following figure shows the hypothetical regression surface, along with the data points used to estimate it.

#library(plotly)

# Define the elliptic paraboloid function: z = (x^2 / a^2) + (y^2 / b^2)
# Create a grid of x and y values
x <- seq(-2, 2, length =200)
y <- seq(-2, 2, length =200)
z <- outer(x, y, function(x, y) (x^2  + y^2 ))

## sample points on the surface to define a data set
x.sim <- sample(x)
y.sim <- sample(y)
z.sample <- (x.sim^2 + y.sim^2) + rnorm(200, 0, 0.5)

# Plot margins
m <- list( l = 50, r = 50, b = 100, t = 100, pad = 4)

# Create the 3D surface plot
plot_ly(x = x, y = y, z = z, type = "surface") %>%  # the surface
  add_markers(x = x.sim,   # sample points near the surface
              y = y.sim, 
              z = z.sample,   
              marker = list(size = 3, color = "darkred")) %>%
  layout(margin = m, 
          title = "Elliptic Paraboloid: Hypothesized Model for Data Generation",
          scene = list(xaxis = list(title = "X"),
                       yaxis = list(title = "Y"),
                       zaxis = list(title = "Z")
                   )
        )

In practice, we only observe data points from an unknown surface. We therefore remove the hypothetical regression surface and visualize just the data points to reveal the data cloud’s structure.

plot_ly() %>%
  add_markers(x = x.sim, y = y.sim, z = z.sample, 
              marker = list(size = 3, color = "darkred")) %>%
  layout( margin = m,
           title = "Sample points from the elliptic paraboloid",
           scene = list( xaxis = list(title = "X"),
                         yaxis = list(title = "Y"),
                         zaxis = list(title = "Z")
                     )
         )

There is a nonlinear relationship between the target variable (\(z\)) and feature variables (\(x_1\) and \(x_2\)). In the following figures, we compare the performance of three models fitted to the same dataset: a single perceptron, a one-hidden-layer perceptron, and a two-hidden-layer perceptron. We then plot their corresponding estimated regression surfaces to visualize the goodness of fit.

#library(neuralnet)
#library(ggplot2)
#library(plotly)  # For 3D visualization

# Generate training data
#set.seed(123)
#x <- seq(-2, 2, length =20)
#y <- seq(-2, 2, length =20)
#z_train <- x_train^2 + y_train^2 # + rnorm(500, sd = 0.1)  # Added noise

## The working data set: we call it the training data set
train.data <- data.frame(x = x.sim, y = y.sim, z = z.sample)

### no hidden layer
hidden0.0 <- neuralnet(z ~ x + y, 
                       data = train.data, 
                       hidden = c(0), 
                       linear.output = TRUE,
                       stepmax = 1e6)  # Increase iterations



### single hidden layer
hidden1.8 <- neuralnet(z ~ x + y, 
                       data = train.data, 
                       hidden = c(8), 
                       linear.output = TRUE,
                       stepmax = 1e6)  # Increase iterations



# Train neural network with 2 hidden layers (8 and 4 neurons)
hidden2.8.4 <- neuralnet(z ~ x + y, 
                       data = train.data, 
                       hidden = c(8,4), 
                       linear.output = TRUE,
                       stepmax = 1e6)  # Increase iterations

# Create test grid to draw the Predicted surface based on different NN models
x.test <- seq(-2, 2, length.out = 200)
y.test <- seq(-2, 2, length.out = 200)
test.grid <- expand.grid(x = x.test, y = y.test)
z.test0.0 <- matrix(predict(hidden0.0, test.grid),ncol=200)
z.test1.8 <- matrix(predict(hidden1.8, test.grid),ncol=200)
z.test2.8.4 <- matrix(predict(hidden2.8.4, test.grid),ncol=200)

The following figure shows the predicted surface from the perceptron model, which produces a linear hyperplane, along with the data points used to estimate the regression surface. The perceptron fits the data poorly.

## set up plot margin
m <- list(l = 50,r = 50, b = 100, t = 100, pad = 4)

# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test0.0, type = "surface") %>%
  add_markers(data = train.data, 
              x = ~x, y = ~y, z = ~z, 
              marker = list(size = 3, color = "darkred")) %>%
  layout(title = 'Perceptron Model (with no hidden layer)',
         autosize = T, 
         #width = 500, 
         #height = 500,
         margin = m, 
         scene = list(xaxis = list(title = "X"),
                     yaxis = list(title = "Y"),
                     zaxis = list(title = "Z")))

Next, we fit a one-hidden-layer perceptron with 8 nodes to the same dataset. The following figure shows both the estimated regression surface and the data points. We observe that the predicted surface closely approximates the hypothesized regression surface. Note that the number of nodes in the hidden layer is a tunable hyperparameter that can be optimized to improve model performance.

# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test1.8, type = "surface") %>%
  add_markers(data = train.data, 
              x = ~x, y = ~y, z = ~z, 
              marker = list(size = 3, color = "darkred")) %>%
  layout(title = 'Estimated One-hidden-layer Perceptron (with 8 nodes)',
         autosize = T, 
         #width = 500, 
         #height = 500,
         margin = m, 
         scene = list(xaxis = list(title = "X"),
                     yaxis = list(title = "Y"),
                     zaxis = list(title = "Z")))

Finally, we examine a two-hidden-layer perceptron architecture, with 8 nodes in the first hidden layer and 4 nodes in the second hidden layer, trained on the same data set.

# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test2.8.4, type = "surface") %>%
  add_markers(data = train.data, 
              x = ~x, y = ~y, z = ~z, 
              marker = list(size = 3, color = "darkred")) %>%
  layout(title = 'Estimated Two-hidden-layer Perceptron (with 8 and 4 nodes)',
         autosize = T, 
         #width = 500, 
         #height = 500,
         margin = m, 
         scene = list(xaxis = list(title = "X"),
                     yaxis = list(title = "Y"),
                     zaxis = list(title = "Z")))

The figure above shows both the estimated prediction surface and the original data points. We observe a significant discrepancy between the estimated regression surface and the hypothesized (true) regression surface. Upon closer examination, we find that the estimated regression surface yields small residuals, as the data points lie close to the fitted surface. This suggests potential overfitting in the two-hidden-layer perceptron model.

3 MLP Modeling Process

MLPs can learn complex, non-linear relationships due to their multiple layers of interconnected neurons. However, designing and training an effective MLP requires careful consideration of data preprocessing, architecture selection, optimization techniques, and other strategies.

3.1 Data Preprocessing

Before training an MLP, the input data must be properly prepared to make sure the MLP learns efficiently.

3.1.1 Feature Scaling

MLPs are sensitive to feature scales, so normalization (e.g., Min-Max scaling) or standardization (e.g., Z-score normalization) is typically applied to ensure stable gradient descent optimization.

Whether the target variable must be rescaled in a neural network depends on the context, but it is often strongly recommended for optimal performance.

The scaling of the target is necessary when

  • Output Activation Constraints: If using a sigmoid (bounded to [0, 1]) or tanh (bounded to [-1, 1]) output activation, the target variable must be scaled to match these ranges.

  • Loss Function Sensitivity: Loss functions like MSE (Mean Squared Error) are sensitive to scale. Large target values can lead to unstable gradients or slow convergence.

  • Regularization Terms: \(L_1\)/\(L_2\) regularization penalizes large weights. Unscaled targets may force the network to learn disproportionately large weights to compensate.

While not always mandatory, rescaling the target variable typically improves training stability, speed, and model performance—especially for bounded activations or large-value ranges. Always validate with your specific architecture and data.

3.1.2 Handling Missing Data

Handling missing values in Multilayer Perceptrons (MLPs) requires careful consideration, as neural networks typically expect complete input data.

All imputation methods discussed in the previous section can be used to preprocess data with missing values for MLP modeling. The general recommendation is to use stochastic multiple imputation techniques, such as MICE (Multiple Imputation by Chained Equations) and other random replacement methods that make use of the probability distribution of the underlying feature variables.

3.1.3 Categorical Encoding

Categorical encoding transforms non-numeric variables into numerical representations for machine learning.

  • For nominal data (no inherent order), common methods include one-hot encoding, which creates binary columns for each category (ideal for <15 categories), and target encoding, which replaces categories with the mean of the target variable (better for high-cardinality features but requires careful validation to avoid leakage).

  • For ordinal data (natural order), ordinal encoding assigns integers while preserving the logical sequence (e.g., Small=1, Medium=2).

  • High-cardinality features (e.g., ZIP codes) may use frequency encoding (replacing categories with their occurrence counts) or embedding layers in neural networks to capture complex relationships.

The choice depends on the model type and data characteristics:

  • Tree-based models (e.g., Random Forests) often handle label encoding well.

  • Linear models and neural networks typically require one-hot or target encoding.

Cross-validation is essential to evaluate the impact of encoding strategies on model performance.

3.1.4 Data Splitting

Proper data splitting is crucial for training Multilayer Perceptrons (MLPs) to ensure robust model evaluation and prevent overfitting. For cross-sectional data, a random splitting is required:

  • Two-way splitting for training and testing data sets. The training and test data ratio is usually 70%-30% or 80%-20%.

  • Nested random splitting for cross-validation involves two layers of data partitioning: an outer loop for performance evaluation and an inner loop for hyperparameter tuning. This approach ensures unbiased model assessment by preventing data leakage between the tuning and evaluation phases.

There are other random data splitting methods based on the data structures that require special design so that the randomly split data retain the same probability structure of the data set.

3.2 MLP Architecture Desing

In neural networks, MLP (Multilayer Perceptron) size and depth refer to its architecture, particularly the number of neurons (size) and the number of hidden layers (depth). These factors significantly impact the model’s capacity, training dynamics, and generalization ability.

  • Shallow networks (1-2 hidden layers) may suffice for simple tasks. Shallow networks
    • can approximate any continuous function (Universal Approximation Theorem).
    • often sufficient for simple tasks (e.g., small tabular datasets).
    • are less prone to overfitting but may underfit complex data.
  • Deep networks can model complex patterns but risk overfitting and increased computational cost. Deep networks are
    • better at learning hierarchical representations.
    • useful for high-dimensional data (e.g., images, NLP after feature extraction).
    • more computationally expensive and prone to overfitting (requires regularization like Dropout, L2).
  • MLP Size (Number of Neurons per Layer) is also impactful on the model performance.
    • Wider Layers (more neurons) increase model capacity, allowing more complex function approximation. It can lead to overfitting if not regularized and use more computation resources (more parameters).
    • Narrower Layers (fewer neurons) are less expressive but faster to train but may lead to underfitting complex patterns.
  • Choosing Depth & Size is based on the following general recommendations
    • Start with 1-3 hidden layers and adjust based on performance.
    • Use similar or decreasing layer sizes.
    • Wider layers early can help feature extraction; deeper networks refine abstractions.

3.3 Activation Functions

Activation functions serve distinct purposes in hidden and output layers. In hidden layers, they introduce nonlinear transformations to capture complex patterns in the data. The output layer’s activation function is chosen specifically to produce predictions compatible with the target variable’s characteristics.

3.3.1 Hidden Layer Activation

There are several activation functions for the hidden layer. The ReLU (Rectified Linear Unit) is widely used due to its computational efficiency and is the default for many MLP libraries. The following table outlines some of the commonly used activation functions for hidden layers.

Activation Best For Pros Cons Recommendation
ReLU Default choice Fast, avoids vanishing gradient (for x > 0) “Dying ReLU” (dead neurons) First try in most MLPs
Leaky ReLU Deep networks Fixes dying ReLU Slightly slower than ReLU Use if ReLU fails
GELU Transformers, deep learning Smooth, better gradient flow More compute-heavy Best for modern deep nets (e.g., BERT, GPT)
Swish Experimental alternative Can outperform ReLU Slower than ReLU Try if tuning performance
SELU Self-normalizing networks No BatchNorm needed Requires careful initialization Use only in specific architectures

Avoid using Sigmoid/Tanh in hidden layers that cause vanishing gradients in deep networks.

3.3.2 Output Layer Activation

Depending on the type of the target response, the commonly used activation functions are summarized in the following table.

Task Activation Why? Example Use Case
Regression (unbounded) Linear (no activation) Outputs any real number Predicting house prices
Regression (0 to 1) Sigmoid Bounds output to [0, 1] Predicting probabilities
Binary Classification Sigmoid Outputs probability (0 or 1) Spam detection
Multiclass Classification Softmax Probabilities sum to 1 MNIST digit classification
Multilabel Classification Sigmoid (per class) Independent probabilities Image tagging

Never use ReLU in the output layer unless outputs must be ≥0, e.g., count prediction like Poisson regression).

3.3.3 Guideline for Hidden and Output Layers

The following table provides a guideline for choosing appropriate activation functions in MLP.

Scenario Hidden Layer Output Layer
General MLP ReLU Task-dependent (see above)
Deep Network GELU/Leaky ReLU Task-dependent
Transformer Model GELU Softmax/Sigmoid
Self-Normalizing Net SELU Task-dependent

3.4 Training, Optimization

Effective training requires proper optimization techniques and hyperparameter tuning.

  • Loss Function Selection is dependent on the applications:
    • Classification: Cross-entropy loss
    • Regression: Mean Squared Error (MSE) or Mean Absolute Error (MAE)
  • Optimizer Choice is also dependent on the applications and the required computational resources for the applications.
    • Stochastic Gradient Descent (SGD): Basic but requires careful learning rate tuning.
    • Adaptive Optimizers (Adam, RMSprop): Automatically adjust learning rates, often leading to faster convergence. A too-high learning rate causes instability, while a too-low rate slows convergence.
  • Hyperparameter Tuning Methods like grid search, random search, or Bayesian optimization help find optimal configurations efficiently.


3.5 Relative Improvement in Performance

When evaluating machine learning models, it is essential to quantify how much a proposed neural network (NN) model improves upon a simpler base model (e.g., linear regression for regression tasks, and logistic regression for classification). Measuring relative improvement helps determine whether the added complexity of a neural network justifies its adoption. We next briefly discuss key methods for computing and interpreting relative improvement, along with practical considerations. Recall that performance metrics for regression and classification are listed.

  • Classification: Accuracy, Precision, Recall, F1-score, ROC-AUC.
  • Regression: MSE, RMSE, \(R^2\).

Relative improvement measures how much better (or worse) a neural network performs compared to a baseline model. It is typically expressed as a percentage reduction in error (for regression) or a percentage increase in accuracy/precision/recall (for classification). The general formula is given by

\[ \text{Relative Improvement} = \frac{\text{Metric}_{\text{Base}}-\text{Metric}_{\text{NN}}}{\text{Metric}_{\text{Base}}} \]

As an example, compare the RMSE between linear regression (fit it to the scaled data) and NN model. Assume the base model RMSE = 10.0 and NN model RMSE = 7. Then

\[ \text{Improvement} = \frac{10-7}{10} = 30\%. \]

Interpretation: The NN reduces prediction error by 30% compared to linear regression.


4 MLP Regression

This section focuses on implementing MLP regression using the Boston Housing dataset, along with the R neuralnet library and other supporting libraries for data preparation and visualization. We will follow the basic steps outlined in the previous section. We will fit one-hidden-layer and two-hidden-layer MLP to predict the median house value.

We use min-max scaling method for all numerical variables. All features are numerical, no categorical encoding is needed. We use random splitting (70-30) to create training and testing data sets.

# Load necessary libraries
# library(neuralnet)
# library(MASS)       # For Boston Housing dataset
# library(ggplot2)    # For visualization
# library(caret)      # Only for data splitting (we won't use its modeling functions)
# Load Boston Housing dataset
data(Boston)

# Check structure and summary
#str(Boston)
#summary(Boston)

# Feature scaling - normalize all variables to [0,1] range
normalize <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}

boston.scaled <- as.data.frame(lapply(Boston, normalize))

# Set seed for reproducibility
set.seed(123)
N <- length(boston.scaled$medv)
# Create train-test split (70-30)
train.reg.index <- sample(1:N,  floor(0.7*N), replace = FALSE)
train.reg.data <- boston.scaled[train.reg.index, ]
test.reg.data <- boston.scaled[-train.reg.index, ]

# Check dimensions
#dim(train_data)
#dim(test_data)

4.1 One-hidden-layer Perceptron

We first build a single hidden layer perceptron model. We will tune three hyperparameters: the number of nodes in the hidden layer, learning rate, and activation function using grid search. The performance metric used to select the optimal combination of values of hyperparameters is RMSE.

# Define grid of hyperparameters
hyper.grid.reg <- expand.grid(
  layer1 = c(5, 10, 15),
  learning.rate = c(0.01, 0.1),
  activation = c("logistic", "tanh")
)

# Initialize results storage
rmse = NULL
#layer1 = NULL
#learningrate = NULL
#activation = NULL

best.reg.rmse <- Inf
best.reg.model <- NULL

# Perform grid search
for(i in 1:nrow(hyper.grid.reg)) {
  # Get current configuration
  layer <- hyper.grid.reg$layer1[i]
  lr <- hyper.grid.reg$learning.rate[i]
  act <- hyper.grid.reg$activation[i]
  
  # Train model
  set.seed(123)
  model.reg <- neuralnet(
      medv ~ .,
      data = train.reg.data,
      hidden = layer,
      act.fct = act,
      linear.output = TRUE,  # For regression
      learningrate = lr,
      algorithm = "rprop+",
      stepmax = 1e5 )
  

    # Make predictions
    preds.reg <- predict(model.reg, test.reg.data[, -ncol(test.reg.data)])
    
    # Calculate RMSE
    rmse.reg <- sqrt(mean((preds.reg - test.reg.data$medv)^2))
    
    # Store results
    rmse[i] = rmse.reg
    
    # Update best model
    if(rmse.reg < best.reg.rmse) {
      best.reg.rmse <- rmse.reg
      best.reg.model <- model.reg 
      best.reg.params <- hyper.grid.reg[i, ]
    }
}

results.regNN <- hyper.grid.reg
results.regNN$rmse <- rmse


# View results sorted by RMSE
pander(results.regNN[order(results.regNN$rmse), ][1,])
  layer1 learning.rate activation rmse
3 15 0.01 logistic 0.08035

With the above combination of optimal hyperparameter values, we train the final single hidden layer perceptron model.

# Train the final model with best parameters
final.reg.model <- neuralnet(
  medv ~ .,
  data = train.reg.data,
  hidden = best.reg.params$layer1,
  act.fct = best.reg.params$activation,
  linear.output = TRUE,
  learningrate = best.reg.params$learning_rate,
  algorithm = "rprop+",
  stepmax = 1e5
)

# Plot the neural network
#plot(final.reg.model)

The above NN plot shows the architecture of the final one-hidden-layer perceptron model. Next, we will use it to make predictions.

# Make predictions on test set
pred.NN1 <- predict(final.reg.model, test.reg.data[, -ncol(test.reg.data)])

# Calculate performance metrics
rmse.NN1 <- sqrt(mean((pred.NN1  - test.reg.data$medv)^2))
mae.NN1 <- mean(abs(pred.NN1  - test.reg.data$medv))
r.squared.NN1 <- cor(pred.NN1 , test.reg.data$medv)^2

# cat("Performance Metrics:\n")
# cat("RMSE:", rmse, "\n")
# cat("MAE:", mae, "\n")
# cat("R-squared:", r_squared, "\n")

# Plot predictions vs actual
plot.NN1.data <- data.frame(
  Actual = test.reg.data$medv,
  Predicted = pred.NN1 
)

ggplot(plot.NN1.data, aes(x = Actual, y = Predicted)) +
  geom_point() +
  geom_abline(intercept = 0, slope = 1, color = "darkred") +
  annotate("text", x=0.85, y=.2, 
           label=paste("R.sq =", round(r.squared.NN1,4)), color="blue") +
  annotate("text", x=0.85, y=.13, 
           label=paste("RMSE =", round(rmse.NN1,4)), color="blue") +
  annotate("text", x=0.85, y=.06, 
           label=paste("  MAE =", round(mae.NN1,4)), color="blue") +
  ggtitle("Actual vs Predicted Values") +
  theme_minimal()

The figure above demonstrates a strong correlation between the predicted target values and the scaled target values. Both the mean squared error (MSE) and mean absolute error (MAE) metrics are displayed on the plot. Next, we fit a classical linear regression model to the scaled data and perform a comparative analysis between this baseline linear regression and our neural network model.

# library(ggplot2)
# Train linear regression model
lm.model <- lm(medv ~ ., data = test.reg.data)

# Make predictions
lm.predictions <- predict(lm.model, test.reg.data[, -ncol(test.reg.data)])

# Calculate performance metrics
lm.rmse <- sqrt(mean((lm.predictions - test.reg.data$medv)^2))
lm.mae <- mean(abs(lm.predictions - test.reg.data$medv))
lm.r.squared <- cor(lm.predictions, test.reg.data$medv)^2


## improvements
RMSE.imp <- round((lm.rmse - rmse.NN1)/lm.rmse * 100,2)
MAE.imp <- round((lm.mae - mae.NN1)/lm.mae * 100, 2)
Rsq.imp <- round((r.squared.NN1 - lm.r.squared)/lm.r.squared * 100,2)

##
Performance.table <- data.frame(
  LM = c(lm.rmse, lm.mae, lm.r.squared),
  NN.1 = c(rmse.NN1, mae.NN1, r.squared.NN1),
  Improvement.percentage = c(RMSE.imp, MAE.imp, Rsq.imp)
)
rownames(Performance.table) <- c("RMSE", "MAE", "R.square")
pander(Performance.table)
  LM NN.1 Improvement.percentage
RMSE 0.09682 0.08986 7.19
MAE 0.07097 0.0565 20.39
R.square 0.7858 0.8299 5.61
# Plot both predictions
comparison.data <- data.frame(
  Actual = test.reg.data$medv,
  MLP = pred.NN1,
  Linear = lm.predictions
)

ggplot(comparison.data) +
  geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
  geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
  geom_abline(intercept = 0, slope = 1, color = "black") +
  scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
  labs(title = "Model Comparison: Actual vs Predicted",
       x = "Actual Values",
       y = "Predicted Values",
       color = "Model Type") +
  theme(
    plot.margin = ggplot2::margin(40, 20, 20, 20, unit = "pt"),
    plot.title = element_text(hjust = 0.5, 
                              lineheight = 1.1,
                              vjust = 10)
    )

The above scatter plot of the true target values and predicted values based on the two models also shows that the one-hidden-perceptron model outperforms the classic linear regression model.

4.2 Two-hidden-layer Perceptron

This subsection explores whether a two-hidden-layer perception that has more complex architecture could improve the performance.

The model-building process is identical to the previous one-hidden-layer perceptron model. We will not detail the steps as we did in the previous section.

# Define grid of hyperparameters
hyper.grid.NN2 <- expand.grid(
  layer1 = c(5, 10, 15),
  layer2 = c(0, 3, 5),  # 0 means no second layer
  learning.rate = c(0.01, 0.1),
  activation = c("logistic", "tanh")
)

# Initialize results in storage
results <- data.frame()
best.rmse <- Inf
best.model <- NULL

# Perform grid search
for(i in 1:nrow(hyper.grid.NN2)) {
  # Get current configuration
  layer1 <- hyper.grid.NN2$layer1[i]
  layer2 <- hyper.grid.NN2$layer2[i]
  lr <- hyper.grid.NN2$learning.rate[i]
  act <- hyper.grid.NN2$activation[i]
  
  # Create hidden layers vector
  if(layer2 == 0) {
    hidden.layers <- c(layer1)
  } else {
    hidden.layers <- c(layer1, layer2)
  }
  
  # Train model
  set.seed(123)
  model.NN2 <- tryCatch({
    neuralnet(
      medv ~ .,
      data = train.reg.data,
      hidden = hidden.layers,
      act.fct = act,
      linear.output = TRUE,  # For regression
      learningrate = lr,
      algorithm = "rprop+",
      stepmax = 1e5
    )
  }, error = function(e) NULL)
  
  if(!is.null(model.NN2)) {
    # Make predictions
    preds <- predict(model.NN2, test.reg.data[, -ncol(test.reg.data)])
    
    # Calculate RMSE
    rmse <- sqrt(mean((preds - test.reg.data$medv)^2))
    
    # Store results
    results <- rbind(results, data.frame(
      layer1 = layer1,
      layer2 = layer2,
      learning_rate = lr,
      activation = act,
      rmse = rmse
    ))
    
    # Update best model
    if(rmse < best.rmse) {
      best.rmse <- rmse
      best.model <- model.NN2
      best.params <- hyper.grid.NN2[i, ]
    }
  }
}

# View results sorted by RMSE
results[order(results$rmse), ]
# Train the final model with best parameters
final.model.NN2 <- neuralnet(
  medv ~ .,
  data = train.reg.data,
  hidden = if(best.params$layer2 == 0) {
        c(best.params$layer1)} else {
        c(best.params$layer1, best.params$layer2)},
  act.fct = best.params$activation,
  linear.output = TRUE,
  learningrate = best.params$learning.rate,
  algorithm = "rprop+",
  stepmax = 1e5
)

# Plot the neural network
#plot(final.model.NN2)

# Make predictions on test set
pred.NN2 <- predict(final.model.NN2, test.reg.data[, -ncol(test.reg.data)])

# Calculate performance metrics
rmse.NN2 <- sqrt(mean((pred.NN2  - test.reg.data$medv)^2))
mae.NN2 <- mean(abs(pred.NN2  - test.reg.data$medv))
r.squared.NN2 <- cor(pred.NN2 , test.reg.data$medv)^2

## vector of error metric
NN2 <- c(rmse.NN2, mae.NN2, r.squared.NN2)

# Plot predictions vs actual
plot.data.NN2 <- data.frame(
  Actual = test.reg.data$medv,
  Predicted = pred.NN2 
)

ggplot(plot.data.NN2, aes(x = Actual, y = Predicted)) +
  geom_point() +
  geom_abline(intercept = 0, slope = 1, color = "red") +
  ggtitle("Actual vs Predicted Values") +
  theme_minimal()

# Train linear regression model
lm.model <- lm(medv ~ ., data = train.reg.data)

# Make predictions
lm.predictions <- predict(lm.model, test.reg.data[, -ncol(test.reg.data)])

# Calculate performance metrics
lm.rmse <- sqrt(mean((lm.predictions - test.reg.data$medv)^2))
lm.mae <- mean(abs(lm.predictions - test.reg.data$medv))
lm.r.squared <- cor(lm.predictions, test.reg.data$medv)^2

###
rmse.NN2.imp <- (lm.rmse - rmse.NN2)/lm.rmse*100
mae.NN2.imp <- (lm.mae - mae.NN2)/lm.mae * 100
Rsq.NN2.imp <- (r.squared.NN2 - lm.r.squared)/lm.r.squared * 100

NN2.improve <-c(rmse.NN2.imp, mae.NN2.imp, Rsq.NN2.imp)

perf.matrix <-data.frame(
              LM = c(lm.rmse, lm.mae, lm.r.squared),
              NN.1 = c(rmse.NN1, mae.NN1, r.squared.NN1),
              NN.2 = c(rmse.NN2, mae.NN2, r.squared.NN2)
         )

perf.matrix$NN1.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.1)/perf.matrix$LM,2)
perf.matrix$NN2.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.2)/perf.matrix$LM,2)

rownames(perf.matrix) <- c("RMSE", "MAE", "R.sq")
pander(perf.matrix)
  LM NN.1 NN.2 NN1.Improve NN2.Improve
RMSE 0.1067 0.08986 0.09108 15.81 14.67
MAE 0.07848 0.0565 0.05954 28.01 24.13
R.sq 0.7461 0.8299 0.8171 -11.23 -9.51
# Plot both predictions
comparison.data.NN2 <- data.frame(
  Actual = test.reg.data$medv,
  MLP = pred.NN2 ,
  Linear = lm.predictions
)

ggplot(comparison.data.NN2) +
  geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
  geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
  geom_abline(intercept = 0, slope = 1, color = "black") +
  scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
  labs(title = "Model Comparison: Actual vs Predicted",
       x = "Actual Values",
       y = "Predicted Values",
       color = "Model Type") +
  theme_minimal()

5 MLP Classification

The modeling process for MLP classification is identical to that of MLP regression. As we did in the previous section on MLP regression, we will document the modeling process in detail.

The Pima Indian Diabetes dataset contains only numerical feature variables. Therefore, we will apply min-max scaling to all features and convert the target variable into a factor variable.

# Load necessary libraries
# library(neuralnet)
# library(pROC)     # For ROC analysis
# library(ggplot2)  # For visualization

# Load the Pima Indians Diabetes dataset
data("PimaIndiansDiabetes2", package = "mlbench")

## removing records with missing component; imputation should be considered in
## practical applications
diabetes.data <- na.omit(PimaIndiansDiabetes2)  

# Feature scaling - normalize numeric variables to [0,1] range
normalize <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}
# Apply normalization to all numeric columns
numeric.cols <- sapply(diabetes.data, is.numeric)
diabetes.data[numeric.cols] <- lapply(diabetes.data[numeric.cols], normalize)

# Encode the target variable (diabetes) as numeric (0/1)
diabetes.data$diabetes <- ifelse(diabetes.data$diabetes == "pos", 1, 0)

# Two-way data splitting: 70-30%
set.seed(123)  # For reproducibility
sample.size.cls <- floor(0.70 * nrow(diabetes.data))
train.indices.cls <- sample(1:sample.size.cls, size = sample.size.cls, replace = FALSE)

train.data.cls <- diabetes.data[train.indices.cls, ]
test.data.cls <- diabetes.data[-train.indices.cls, ]

To simplify hyperparameter tuning and final model training with the pre-selected MLP architecture for classification, we define a custom function to determine the optimal number of nodes for both single-hidden-layer and double-hidden-layer MLPs. The criterion for selecting the optimal number of nodes is the area under the ROC curve (AUC) as the evaluation metric, though accuracy based on the default 0.5 thresholds could alternatively be used. We choose the logistic activation function while keeping all other hyperparameters at their default values.

# Function to perform grid search for neuralnet
neuralnet.grid.search <- function(train.data, test.data, hidden.layers = 1) {
  # Define the grid of hyperparameters
  if (hidden.layers == 1) {
    hidden.nodes <- c(2, 4, 6, 8, 10)
    grid <- expand.grid(hidden = hidden.nodes)
  } else {
    hidden.nodes <- c(2, 4, 6)
    grid <- expand.grid(hidden1 = hidden.nodes, hidden2 = hidden.nodes)
  }
  
  # Add columns to store results
  grid$accuracy <- NA
  grid$auc <- NA
  
  # Formula for neural network
  nn.formula <- as.formula(paste("diabetes ~", 
                                paste(names(train.data)[!names(train.data) %in% "diabetes"], 
                                      collapse = " + ")))
  
  # Perform grid search
  for (i in 1:nrow(grid)) {
    if (hidden.layers == 1) {
      hidden <- c(grid$hidden[i])
    } else {
      hidden <- c(grid$hidden1[i], grid$hidden2[i])
    }
    
    # Train the model
    nn.model <- neuralnet(
      formula = nn.formula,
      data = train.data,
      hidden = hidden,
      linear.output = FALSE,  # For classification
      act.fct = "logistic",   # Sigmoid activation
      stepmax = 1e6           # Increase max steps for convergence
    )
    
    # Make predictions
    predictions <- predict(nn.model, test.data)
    predicted.classes <- ifelse(predictions > 0.5, 1, 0)
    
    # Calculate accuracy
    accuracy <- mean(predicted.classes == test.data$diabetes)
    
    # Calculate AUC
    roc.obj <- roc(test.data$diabetes, predictions)
    auc.value <- auc(roc.obj)
    
    # Store results
    grid$accuracy[i] <- accuracy
    grid$auc[i] <- auc.value
  }
  return(grid)
}

The performance table of corresponding number of nodes in one-hidden-layer MLP is given below.

# Perform grid search for single hidden layer
grid.results.1layer <- neuralnet.grid.search(train.data=train.data.cls, 
                                             test.data=test.data.cls, 
                                             hidden.layers = 1)
pander(grid.results.1layer)
hidden accuracy auc
2 0.8305 0.8569
4 0.822 0.8659
6 0.839 0.8509
8 0.8559 0.8941
10 0.822 0.873

The optimal number of nodes in the hidden layer is the corresponds to the smallest AUC. Similarly, the performance table of two-hidden-layer MLP is given below.

# Perform grid search for two hidden layers
grid.results.2layer <- neuralnet.grid.search(train.data=train.data.cls, 
                                             test.data=test.data.cls, 
                                             hidden.layers = 2)
pander(grid.results.2layer)
hidden1 hidden2 accuracy auc
2 2 0.8305 0.856
4 2 0.822 0.8879
6 2 0.822 0.8742
2 4 0.8136 0.8166
4 4 0.7881 0.8318
6 4 0.8136 0.8979
2 6 0.8051 0.8956
4 6 0.7797 0.8552
6 6 0.8644 0.9087

One-hidden-layer MLP

We use the optimal number of nodes to fitthe onehidden-layer MLP to the data anf obtain

# Formula for neural network
nn.formula <- as.formula(paste("diabetes ~", 
                              paste(names(train.data.cls)[!names(train.data.cls) %in% "diabetes"], 
                                    collapse = " + ")))

# Train single hidden layer model (using best configuration from grid search)
best.1layer <- grid.results.1layer[which.max(grid.results.1layer$auc), ]

nn.1layer <- neuralnet(
  formula = nn.formula,
  data = train.data.cls,
  hidden = best.1layer$hidden,
  linear.output = FALSE,
  act.fct = "logistic",
  stepmax = 1e6
)
##
#plot(nn.1layer, main = paste("One-hidden-layer with", best.1layer$hidden, "Nodes"))

# Train two hidden layers model (using best configuration from grid search)
best.2layer <- grid.results.2layer[which.max(grid.results.2layer$auc), ]

nn.2layer <- neuralnet(
  formula = nn.formula,
  data = train.data.cls,
  hidden = c(best.2layer$hidden1, best.2layer$hidden2),
  linear.output = FALSE,
  act.fct = "logistic",
  stepmax = 1e6
)
##
#plot(nn.2layer)

In the two model plots above, the Error and Steps values displayed at the bottom represent:

  • Steps: The number of training iterations (epochs) completed during model optimization. Each step corresponds to one complete forward/backward pass and weight update cycle.

  • Errors: The training error reflects the loss function value (typically SSE for regression or cross-entropy for classification). The displayed Error represents the final error value achieved when the optimization process converges.

Next, we write a custom function to extract the performance metrics to assess the global performance through ROC curves and the corresponding areas under the ROC curves.

# Function to evaluate model performance
evaluate.model <- function(model, test.data) {
  # Make predictions
  predictions <- predict(model, test.data)
  predicted.classes <- ifelse(predictions > 0.5, 1, 0)
  
  # Calculate metrics
  accuracy <- mean(predicted.classes == test.data$diabetes)
  confusion.matrix <- table(Predicted = predicted.classes, Actual = test.data$diabetes)
  roc.obj <- roc(test.data$diabetes, predictions)
  auc.value <- auc(roc.obj)
  
  return(list(
    accuracy = accuracy,
    confusion.matrix = confusion.matrix,
    roc.obj = roc.obj,
    auc = auc.value
  ))
}

# Evaluate single hidden layer model
perf.1layer <- evaluate.model(nn.1layer, test.data.cls)
#print(perf.1layer[c("accuracy", "confusion_matrix", "auc")])

# Evaluate two hidden layers model
perf.2layer <- evaluate.model(nn.2layer, test.data.cls)
#print(perf.2layer[c("accuracy", "confusion_matrix", "auc")])

We use classic logistic regression as the base model and compare it with the two MLPs using ROC curves and their corresponding AUC values in the following figure.

# Train logistic regression model (base model)
logit.model <- glm(diabetes ~ ., data = train.data.cls, family = binomial)

# Evaluate logistic regression model
logit.pred <- predict(logit.model, test.data.cls, type = "response")
logit.classes <- ifelse(logit.pred > 0.5, 1, 0)
logit.accuracy <- mean(logit.classes == test.data.cls$diabetes)
logit.roc <- roc(test.data.cls$diabetes, logit.pred)
logit.auc <- auc(logit.roc)

##
roc.1layer <- perf.1layer$roc.obj
roc.2layer <- perf.2layer$roc.obj
roc.logit <- logit.roc

## specificity and sensitivity
sen.1layer <- roc.1layer$sensitivities
spe.1layer <- roc.1layer$specificities
sen.2layer <- roc.2layer$sensitivities
spe.2layer <- roc.2layer$specificities
sen.logit <- roc.logit$sensitivities
spe.logit <- roc.logit$specificities

## AUC
auc.1layer <- roc.1layer$auc
auc.2layer <- roc.2layer$auc
auc.logit <- roc.logit$auc

## Plot ROC curves for comparison
par(pty = "s")   # make a square plot to avaoid distortion
plot(1-spe.1layer, sen.1layer, type = "l", lty = 1,
     col = "blue", 
     xlab = "1 - specificity",
     ylab = "sensitvity",
     main = "ROC Curve Comparison")

lines(1-spe.2layer, sen.2layer, lty = 1, col = "darkred")
lines(1-spe.logit, sen.logit, lty = 1, col = "darkgreen")
legend("bottomright", 
       legend = c(paste("1-layer MLP (AUC =", round(perf.1layer$auc, 3), ")"),
                  paste("2-layer MLP (AUC =", round(perf.2layer$auc, 3), ")"),
                  paste("Logistic Reg (AUC =", round(logit.auc, 3), ")")),
                col = c("blue", "darkred", "darkgreen"), 
                lty = 1, cex = 0.7, bty = "n")

The ROC analysis shows that the classic logistic regression model performs slightly better than the two MLPs. The two MLPs perform equally well, even though the two-hidden-layer MLP has more parameters (weights) and is more complex than the single-hidden-layer MLP. In general, a single-hidden-layer MLP is recommended if an MLP is used for classification.

6 MLP or Classical Models?

We have discussed the MLP architecture and the process of implementing MLPs alongside classic models. Case studies using the Boston Housing Data demonstrate that MLP regression outperforms classic linear regression, while the classic logistic regression performs better than MLP classification in the Pima Indian Diabetes dataset analysis. Next, we will present a general comparison between these models.


MLP Classification vs. Logistic Regression

The comparison between multilayer perceptron (MLP) classifiers and logistic regression highlights a fundamental trade-off between flexibility and simplicity.

  • Logistic regression, as a linear classifier, performs well when decision boundaries are approximately linear, offering high interpretability, computational efficiency, and robustness to overfitting—particularly with limited data.

  • MLPs, with their nonlinear activation functions, can model more complex decision boundaries but require careful tuning of architecture and regularization to avoid overfitting.

  • The added complexity of neural networks may not always be justified for simpler classification tasks. However, MLPs remain valuable when dealing with highly nonlinear data where logistic regression’s linear assumptions fail.


MLP Regression vs. Classic Linear Regression

Similar to their classification counterparts, MLP regression models and classical linear regression serve different purposes depending on the nature of the data.

  • Linear regression is optimal when relationships between predictors and the target variable are linear, providing interpretable coefficients, fast training, and low risk of overfitting.

  • MLP regression, however, excels in capturing nonlinear and interactive effects, making it suitable for more complex regression tasks where linear models underperform.

  • The trade-off lies in computational cost, tuning complexity, and the need for larger datasets to prevent overfitting. If the underlying data structure is unknown, a practical approach is to begin with linear regression as a baseline before considering MLPs if residuals suggest unmodeled nonlinearity.


Single-Hidden-Layer vs. Two-Hidden-Layer MLPs

The comparison between single- and two-hidden-layer MLPs underscores model complexity.

  • Deeper networks theoretically have greater representational power. Case studies found that the two-hidden-layer MLP did not outperform its simpler counterpart, despite having more parameters. This is consistent with the universal approximation theorem, which states that a single hidden layer (with sufficient neurons) can approximate any continuous function.

  • The additional layer introduced unnecessary complexity without gains in accuracy, reinforcing that deeper architectures should only be explored when simpler models prove inadequate—typically in highly nonlinear or high-dimensional problems.


General Recommendations

  • Start simple: Always begin with classical models (linear/logistic regression) as baselines. Their interpretability and efficiency make them ideal for initial exploration.

  • Use MLPs judiciously: Reserve MLPs for cases where linear models underperform due to clear nonlinearity or interaction effects. Prefer single-hidden-layer architectures unless deeper networks demonstrably improve performance.

  • Balance complexity and performance: Avoid unnecessary model complexity—favor the simplest model that achieves satisfactory accuracy.

  • Hybrid approach: Combine linear models with feature engineering or kernel methods before resorting to MLPs, particularly in resource-constrained settings.

Ultimately, model choice should be guided by problem constraints, data structure, and the trade-offs between accuracy, interpretability, and computational cost. A systematic, iterative approach—from linear models to shallow then deep networks—ensures both efficiency and performance.

LS0tDQp0aXRsZTogJ011bHRpbGF5ZXIgTmV1cmFsIE5ldHdvcmtzIGZvciBDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbicNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQgDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmxpYnJhcnkoZTEwNzEpDQp9DQppZiAoIXJlcXVpcmUoIm1tZWxuIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1tZWxuIikNCmxpYnJhcnkobW1lbG4pDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQpsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQpsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmxpYnJhcnkoY2FyZXQpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmxpYnJhcnkocnBhcnQpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQp9DQppZiAoIXJlcXVpcmUoImlwcmVkIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImlwcmVkIikNCmxpYnJhcnkoaXByZWQpDQp9DQppZiAoIXJlcXVpcmUoIm1sYmVuY2giKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibWxiZW5jaCIpDQpsaWJyYXJ5KG1sYmVuY2gpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpDQpsaWJyYXJ5KHBST0MpDQp9DQppZiAoIXJlcXVpcmUoIm5ldXJhbG5ldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQ0KbGlicmFyeShuZXVyYWxuZXQpDQp9DQppZiAoIXJlcXVpcmUoIm5ldXJhbG5ldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQ0KbGlicmFyeShuZXVyYWxuZXQpDQp9DQppZiAoIXJlcXVpcmUoIk5ldXJhbE5ldFRvb2xzIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk5ldXJhbE5ldFRvb2xzIikNCmxpYnJhcnkoTmV1cmFsTmV0VG9vbHMpDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJw0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQoqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiosIG9mdGVuIHJlZmVycmVkIHRvIGFzICoqZGVlcCBsZWFybmluZyBtb2RlbHMqKiBvciAgKiptdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyoqLCBhcmUgYSBwb3dlcmZ1bCBjbGFzcyBvZiBhbGdvcml0aG1zIHdpdGggd2lkZSBhcHBsaWNhdGlvbnMuIFRoZXNlIG5ldHdvcmtzIGNvbnNpc3Qgb2YgaW50ZXJjb25uZWN0ZWQgbGF5ZXJzIG9mIGFydGlmaWNpYWwgIm5ldXJvbnMsIiBlYWNoIHByb2Nlc3NpbmcgaW5mb3JtYXRpb24gYW5kIHBhc3NpbmcgaXQgZm9yd2FyZCB0byBzdWJzZXF1ZW50IGxheWVycy4gVGhlIGZpcnN0IGxheWVyIHJlY2VpdmVzIHJhdyBpbnB1dCBkYXRhLCB3aGlsZSBoaWRkZW4gbGF5ZXJzIHRyYW5zZm9ybSB0aGlzIGRhdGEgdGhyb3VnaCB3ZWlnaHRlZCBjb25uZWN0aW9ucyBhbmQgbm9ubGluZWFyIGZ1bmN0aW9ucywgYWxsb3dpbmcgdGhlIG5ldHdvcmsgdG8gbGVhcm4gY29tcGxleCBwYXR0ZXJucy4gVGhlIGZpbmFsIGxheWVyIHByb2R1Y2VzIHByZWRpY3Rpb25zIG9yIGNsYXNzaWZpY2F0aW9ucy4NCg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL01MUDAucG5nIikNCmBgYA0KDQoNCkluIHN0YXRpc3RpY2FsIG1vZGVsaW5nLCBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyBjYW4gaGFuZGxlIGludHJpY2F0ZSByZWxhdGlvbnNoaXBzIGluIGRhdGEuIFVubGlrZSBsaW5lYXIgcmVncmVzc2lvbiBvciBzaW1wbGUgZGVjaXNpb24gdHJlZXMsICBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrIG1vZGVscyBjYW4gbW9kZWwgaGlnaGx5IG5vbmxpbmVhciBpbnRlcmFjdGlvbnMgYW5kIGFkYXB0IHRvIGxhcmdlLXNjYWxlLCBoaWdoLWRpbWVuc2lvbmFsIGRhdGFzZXRzIHdoZXJlIGNsYXNzaWMgc3RhdGlzdGljYWwgbW9kZWxzIG9mdGVuIHN0cnVnZ2xlLiBUaGVpciBmbGV4aWJpbGl0eSBjb21lcyBmcm9tIHRoZWlyIGFiaWxpdHkgdG8gaXRlcmF0aXZlbHkgYWRqdXN0IHdlaWdodHMgZHVyaW5nIHRyYWluaW5nLCByZWZpbmluZyB0aGVpciBwcmVkaWN0aW9ucyBhcyB0aGV5IGVuY291bnRlciBtb3JlIGRhdGEuDQoNCkRlc3BpdGUgdGhlaXIgc3RyZW5ndGhzLCBtdWx0aWxheWVyIG5ldXJhbCBuZXR3b3JrcyByZXF1aXJlIGNhcmVmdWwgdHVuaW5nIGFuZCBsYXJnZSBkYXRhc2V0cyB0byBwZXJmb3JtIHdlbGwuIA0KDQoqIE92ZXJmaXR0aW5n4oCUd2hlcmUgYSBtb2RlbCBtZW1vcml6ZXMgdHJhaW5pbmcgZGF0YSBidXQgZmFpbHMgb24gbmV3IGlucHV0c+KAlGlzIGEgY29tbW9uIGNoYWxsZW5nZSwgbWl0aWdhdGVkIGJ5IHRlY2huaXF1ZXMgbGlrZSBkcm9wb3V0IGFuZCByZWd1bGFyaXphdGlvbi4gDQoNCiogVGhlaXIgKipibGFjayBib3gqKiBuYXR1cmUgY2FuIGFsc28gbWFrZSBpbnRlcnByZXRpbmcgcmVzdWx0cyBkaWZmaWN1bHQsIHBvc2luZyBjaGFsbGVuZ2VzIGluIGZpZWxkcyB3aGVyZSBleHBsYWluYWJpbGl0eSBpcyBjcnVjaWFsLCBzdWNoIGFzIGhlYWx0aGNhcmUgb3IgZmluYW5jZS4gDQoNCioqTUxQKipzIGFyZSBhIGZvdW5kYXRpb25hbCBhcmNoaXRlY3R1cmUgaW4gbW9kZXJuIG1hY2hpbmUgbGVhcm5pbmcgYW5kIGFydGlmaWNpYWwgaW50ZWxsaWdlbmNlLiBUaGVpciBhYmlsaXR5IHRvIGFwcHJveGltYXRlIGNvbXBsZXgsIG5vbmxpbmVhciBmdW5jdGlvbnMgbWFrZXMgdGhlbSBoaWdobHkgdmVyc2F0aWxlIGZvciBhIHdpZGUgcmFuZ2Ugb2YgcmVhbC13b3JsZCBwcm9ibGVtcy4gDQoNClRoZXJlIGFyZSB2YXJpb3VzIGFkYXB0YXRpb25zIGFuZCBleHRlbnNpb25zIG9mIE1MUHMsIGVhY2ggZGV2ZWxvcGVkIGZvciBzcGVjaWZpYyBwdXJwb3NlcyBhY3Jvc3MgZGlmZmVyZW50IGZpZWxkcy4gQSBzb2xpZCB1bmRlcnN0YW5kaW5nIG9mIE1MUHMgaXMgZXNzZW50aWFsIGZvciBncmFzcGluZyB0aGUgdW5kZXJseWluZyBwcmluY2lwbGVzIG9mIG1hbnkgc3BlY2lhbGl6ZWQgbmV1cmFsIG5ldHdvcmsgYWxnb3JpdGhtcy4NCg0KXA0KDQojIEFyY2hpdGVjdHVyZSBvZiBNTFANCg0KQSBNdWx0aWxheWVyIFBlcmNlcHRyb24gKE1MUCkgaXMgZ2VuZXJhbGl6ZWQgZnJvbSB0aGUgcGVyY2VwdHJvbiBieSBhZGRpbmcgb25lIG9yIG1vcmUgaGlkZGVuIGxheWVycyBiZXR3ZWVuIHRoZSBpbnB1dCBhbmQgb3V0cHV0IGxheWVycy4gVGhhdCBpcywgYW4gTUxQIGhhcyB0aHJlZSBtYWpvciBjb21wb25lbnRzOg0KDQotIEFuICoqaW5wdXQgbGF5ZXIqKg0KLSBPbmUgb3IgbW9yZSAqKmhpZGRlbiBsYXllcnMqKiANCi0gQW4gKipvdXRwdXQgbGF5ZXIqKg0KDQpFYWNoIGxheWVyIGNvbnNpc3RzIG9mIG11bHRpcGxlICoqbmV1cm9ucyAobm9kZXMpKiogY29ubmVjdGVkIHZpYSB3ZWlnaHRzLCB3aXRoIG5vbmxpbmVhciBhY3RpdmF0aW9uIGZ1bmN0aW9ucyB0aGF0IGNhcHR1cmUgY29tcGxleCByZWxhdGlvbnNoaXBzIGJldHdlZW4gZmVhdHVyZXMgYW5kIHRoZSB0YXJnZXQsIHRoZXJlYnkgaW1wcm92aW5nIHRoZSBhbGdvcml0aG0ncyBwZXJmb3JtYW5jZS4NCg0KSWYgYW4gTUxQIGhhcyBvbmx5IG9uZSBoaWRkZW4gbGF5ZXIsIHRoaXMgTUxQIGlzIHVzdWFsbHkgY2FsbGVkICoqc2hhbGxvdyBuZXVyYWwgbmV0d29yayoqLiAqKkRlZXAgbmV1cmFsIG5ldHdvcmtzKiogaGF2ZSB0d28gb3IgbW9yZSBoaWRkZW4gbGF5ZXJzLg0KDQpJbiB0aGUgc3Vic2VxdWVudCBzZWN0aW9ucywgd2Ugd2lsbCBicmllZmx5IGludHJvZHVjZSB0aGUgdGhlb3J5IG9mIG5ldXJhbCBuZXR3b3JrcyB3aXRoIGFuIGVtcGhhc2lzIG9uIHNoYWxsb3cgbmV0d29ya3MuIFNvbWUgY2FzZSBzdHVkaWVzIHdpbGwgYWxzbyBiZSBpbmNsdWRlZCB0byBkZW1vbnN0cmF0ZSB0aGUgYXBwbGljYXRpb24gb2YgbmV1cmFsIG5ldHdvcmsgbW9kZWxzIGluIHJlZ3Jlc3Npb24gYW5kIGNsYXNzaWZpY2F0aW9uLg0KDQoNCg0KIyMgTWF0aGVtYXRpY3Mgb2YgTUxQDQoNCkZvciBpbGx1c3RyYXRpb24sIHdlIHVzZSB0aGUgZm9sbG93aW5nIG9uZS1oaWRkZW4tbGF5ZXIgTUxQIHJlZ3Jlc3Npb24gdG8gZXhwbGFpbiB0aGUgbWF0aGVtYXRpY2FsIGZvdW5kYXRpb25zIG9mIHRoZSBhbGdvcml0aG0uIFRoZSBvYmplY3RpdmUgaXMgdG8gcHJlZGljdCB0aGUgdGFyZ2V0IHZhcmlhYmxlIA0KJHkkIHVzaW5nIGlucHV0IGZlYXR1cmVzICRce3hfMSwgeF8yLCBcY2RvdHMsIHhfbiBcfSQuIElmIGFjdGl2YXRpb24gZnVuY3Rpb25zICRccGhpXzEoXGNkb3QpJCBhbmQgJFxwaGkoXGNkb3QpJCBhcmUgY29ycmVjdGx5IHNwZWNpZmllZCwgdGhlbiB0aGUgdGFyZ2V0IGNhbiBiZSBleHByZXNzZWQgYXMgYSBjb21wb3NpdGlvbiBvZiB3ZWlnaHRzIHRocm91Z2ggZnVuY3Rpb24gY29tcG9zaXRpb24uDQoNCg0KYGBge3IgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9NTFAwMS5wbmciKQ0KYGBgDQoNCkluIGVzc2VuY2UsIE1MUCBpcyBhIHByb2JsZW0gb2YgYXBwcm94aW1hdGluZyBhIG11bHRpLXZhcmlhYmxlIGZ1bmN0aW9uLiBBc3N1bWUgdGhhdCB0aGVyZSBhcmUgJG4kIGZlYXR1cmVzIGluIHRoZSBzY2VuYXJpbyBvZiB0aGUgYWJvdmUgb25lLWhpZGRlbi1sYXllciBNTFAsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdGFyZ2V0IHZhcmlhYmxlICR5JCBpcyBjaGFyYWN0ZXJpemVkIGJ5ICR5ID0gZih4XzEsIHhfMiwgXGNkb3RzLCB4X246IFxtYXRoYmZ7d30pJCwgd2hlcmUgJGYoXGNkb3QpJCBpcyB1bmtub3duIGFuZCAkXG1hdGhiZnt3fSQgaXMgdGhlIHZlY3RvciBvZiB1bmtub3duIHBhcmFtZXRlcnMuIFRoYXQgaXMsIGluIHRoZSBhYm92ZSBNTFAsIHRoZSB0YXJnZXQgY2FuIGJlIGV4cHJlc3NlZCBhcw0KDQokJA0KeSA9IGYoeF8xLCB4XzIsIFxjZG90cywgeF9uOiBcbWF0aGJme3d9KSA9IFxwaGlfMltccGhpXzEoeF8xLCB4XzIsIFxjZG90cywgeF9uOiBcbWF0aGJme3d9KV0uDQokJA0KDQo8Zm9udCBjb2xvciA9ICJyZWQiPioqXGNvbG9ye3JlZH1Ob3RlIHRoYXQgJGYoXGNkb3QpJCBpcyBhbiBhcmJpdHJhcnkgZnVuY3Rpb24gJGY6IFxtYXRoYmJ7Un1ebiBccmlnaHRhcnJvdyBcbWF0aGJie1J9JC4qKjwvZm9udD4gRm9yIGEgZ2l2ZW4gZGF0YSBzZXQgJFx7KHhfezFpfSwgeF97Mml9LCBcY2RvdHMsIHhfe25pfSlcfV97aT0xfV5uJCBhbmQgZXN0aW1hdGVkICRcaGF0e1xtYXRoYmZ7d319JCwgZGVwZW5kaW5nIG9uIGFwcGxpY2F0aW9ucywgdGhlIGxvc3MgZnVuY3Rpb24gaXMgZGVmaW5lZCByZXNwZWN0aXZlbHkgYnkNCg0KKiAqKkNvbnRpbnVvdXMgUmVncmVzc2lvbiAoTVNFKSoqDQoNCiQkDQogXG1hdGhjYWx7TH0gPSBcZnJhY3sxfXtOfSBcc3VtX3tjPTF9XkMgKHlfaSAtIFxoYXR7eX1faSleMiANCiQkDQoNCiogKipEaXNjcmV0ZSBDbGFzc2lmaWNhdGlvbiAoQ3Jvc3MtZW50cm9weSkqKg0KDQokJA0KIFxtYXRoY2Fse0x9ID0gLSBcc3VtX3tjPTF9XkMgIHlfYyBcbG9nKFxoYXR7eX1fYykNCiQkDQpmb3IgJEMkIGNsYXNzZXMuIFVzaW5nIHRoZSBncmFkaWVudCBkZWNlbnQgbWV0aG9kLCB3ZSBjYW4gZXhwcmVzcyB0aGUgd2VpZ2h0IHVwZGF0aW5nIGZvcm11bGEgaW4gdGhlIGZvbGxvd2luZw0KDQokJA0KXG1hdGhiZntXfV57KGorMSl9IFxsZWZ0YXJyb3cgXG1hdGhiZntXfV57KGopfSAtIFxldGEgXGZyYWN7XHBhcnRpYWwgXG1hdGhjYWx7TH19e1xwYXJ0aWFsIFxtYXRoYmZ7V319XEJpZ3xfe1xtYXRoYmZ7V309XG1hdGhiZntXfV57KGopfX0NCiQkDQp3aGVyZSAkXGV0YSQgaXMgdGhlIGh5cGVycGFyYW1ldGVyIG9mICoqbGVhcm5pbmcgcmF0ZSoqLiAgDQoNCg0KIyMgVW5pdmVyc2FsIEFwcHJveGltYXRpb24gVGhlb3JlbQ0KDQpUaGUgKipVbml2ZXJzYWwgQXBwcm94aW1hdGlvbiBUaGVvcmVtKiogaXMgYSBmdW5kYW1lbnRhbCByZXN1bHQgaW4gdGhlIHRoZW9yeSBvZiBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3Jrcywgc3RhdGluZyB0aGF0IGEgZmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsgd2l0aCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgY29udGFpbmluZyBhIGZpbml0ZSBudW1iZXIgb2YgbmV1cm9ucyBjYW4gYXBwcm94aW1hdGUgY29udGludW91cyBmdW5jdGlvbnMgb24gY29tcGFjdCBzdWJzZXRzIG9mICRcbWF0aGJie1J9Xm4kIHVuZGVyIG1pbGQgY29uZGl0aW9ucyBvbiB0aGUgYWN0aXZhdGlvbiBmdW5jdGlvbi4gVGhlIGZvcm1hbCBzdGF0ZW1lbnQgaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZzogICAgDQoNCioqQ3liZW5rbydzIFRoZW9yZW0gKDE5ODkpKio6IExldCAkXHBoaShcY2RvdCkkIGJlIGEgY29udGludW91cywgbm9uLWNvbnN0YW50IGFjdGl2YXRpb24gZnVuY3Rpb24gKGUuZy4sIHNpZ21vaWQpLiBHaXZlbiBhbnkgY29udGludW91cyBmdW5jdGlvbiAkZjogWzAsMV1ebiBccmlnaHRhcnJvdyBcbWF0aGJie1J9JCBhbmQgJFxlcHNpbG9uID4gMCQsIHRoZXJlIGV4aXN0cyBhICoqc2luZ2xlLWhpZGRlbi1sYXllciBNTFAqKiAobXVsdGlsYXllciBwZXJjZXB0cm9uKSB3aXRoIGZpbml0ZWx5IG1hbnkgaGlkZGVuIG5vZGVzIChuZXVyb25zKSwgZGVub3RlZCBieSAkTihcbWF0aGJme3h9KSQsIHN1Y2ggdGhhdCANCg0KJCQNClxzdXBfe1xtYXRoYmZ7eH1caW4gWzAsMV1ebn0gXGJpZ3xmKFxtYXRoYmZ7eH0pIC0gTihcbWF0aGJme3h9KSBcYmlnfCA8IFxlcHNpbG9uLg0KJCQNCg0KVGhlIGFib3ZlIGFuYWx5c2lzIGFzc3VtZXMgdGhlIHN1cHBvcnQgb2YgdGhlIG11bHRpdmFyaWFibGUgZnVuY3Rpb24gaXMgJFswLDFdXm4kLiBUaGlzIGNhbiBiZSBlYXNpbHkgZ2VuZXJhbGl6ZWQgdG8gYW55IGZ1bmN0aW9uIHdpdGggZ2VuZXJhbCBmaW5pdGUgc3VwcG9ydCAkW2EtMSwgYl8xXVx0aW1lcyBbYV8yLCBiXzJdIFx0aW1lcyBcY2RvdHNcdGltZXNbYV9rLCBiX2tdJCB2aWEgYSBsaW5lYXIgdHJhbnNmb3JtYXRpb24uIEZvciBleGFtcGxlLCBhc3N1bWUgJGZfayh4X2spJCBoYXMgc3VwcG9ydCAkW2FfaywgYl9rXSQsIHdlIGNhbiB1c2UgbGluZWFyIHRyYW5zZm9ybWF0aW9uICR5X2sgPSAoeF9rIC0gYV9rKS8oYl9rIC0gYV9rKSQgd2hpY2ggaW1wbGllcyB0aGF0ICR5X2sgXGluIFswLDFdJC4gDQoNClRoaXMgZXhwbGFpbnMgd2h5IGEgKipvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24qKiBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24gZGVmaW5lZCBvbiBhIGZpbml0ZSBzdXBwb3J0IHVzaW5nIGZpbml0ZWx5IG1hbnkgbm9kZXMgaW4gdGhlIGhpZGRlbiBsYXllci4gRm9yIGZ1bmN0aW9ucyB3aXRoIGluZmluaXRlIHN1cHBvcnQsIHdlIGNhbiBmaXJzdCBhcHByb3hpbWF0ZSB0aGVtIG9uIGEgZmluaXRlIGRvbWFpbiBhbmQgdGhlbiBhcHBseSB0aGUgKip1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtKiogdG8gYWNoaWV2ZSB0aGUgZGVzaXJlZCByZXN1bHQuDQoNCg0KUmVjYWxsIHRoYXQsIHRoZSBvYmplY3RpdmUgb2YgcmVncmVzc2lvbiBhbmFseXNpcyBpcyB0byBlc3RpbWF0ZSB0aGUgKip1bmtub3duKiogcmVncmVzc2lvbiBmdW5jdGlvbiBiYXNlZCBvbiBhIHNhbXBsZSB0YWtlbiBmcm9tIHRoZSB1bmRlcmx5aW5nIHBvcHVsYXRpb24gYW5kIHRoZW4gdXNlIHRoZSAqKmVzdGltYXRlZCoqIHJlZ3Jlc3Npb24gZnVuY3Rpb24gdG8gDQoNCiogYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmZWF0dXJlIHZhcmlhYmxlcyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZTsNCiogcHJlZGljdCB0aGUgdmFsdWUgb2YgdGhlIHRhcmdldCB2YXJpYWJsZSBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSBpbnB1dCBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KVGhlICoqdW5pdmVyc2FsIGFwcHJveGltYXRpb24gdGhlb3JlbSoqIGZvciBzaGFsbG93IG5ldHdvcmtzIGd1YXJhbnRlZXMgdGhlaXIgY2FwYWJpbGl0eSBhcyBhICoqZnVuY3Rpb24gYXBwcm94aW1hdG9yKiosIG1ha2luZyBpdCBhIG5hdHVyYWwgdGhlb3JldGljYWwgZm91bmRhdGlvbiBmb3IgbmV1cmFsIG5ldHdvcmsgcmVncmVzc2lvbiBtZXRob2RzLg0KDQoNCg0KIyMgU29tZSBHcmFwaGljYWwgRGVtb25zdHJhdGlvbnMNCg0KVGhpcyBzdWJzZWN0aW9uIHVzZXMgYSBzaW11bGF0aW9uIGFwcHJvYWNoIHRvIGRlbW9uc3RyYXRlIHdoeSBNTFAgaXMgYWxzbyBhIG5vbi1jbGFzc2ljYWwgZmFtaWx5IG9mIGFsZ29yaXRobWljIHJlZ3Jlc3Npb24gbW9kZWxzLiBXZSB0YWtlIGEgc2V0IG9mIHBvaW50cyBvbiB0aGUgc3VyZmFjZSBvZiBhIGh5cG90aGVzaXplZCBlbGxpcHRpYyBwYXJhYm9sb2lkIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdGhlbiB1c2UgTUxQIHRvIGVzdGltYXRlIHRoZSBoeXBvdGhldGljYWwgZWxsaXB0aWMgcGFyYWJvbG9pZCBtb2RlbC4gVGhlIGVsbGlwdGljIHBhcmFib2xvaWQgdXNlZCB0byBnZW5lcmF0ZSBkYXRhIGlzIGdpdmVuIGJ5DQoNCiQkDQp6ID0geF8xXjIgKyB4XzJeMg0KJCQNClRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBoeXBvdGhldGljYWwgcmVncmVzc2lvbiBzdXJmYWNlLCBhbG9uZyB3aXRoIHRoZSBkYXRhIHBvaW50cyB1c2VkIHRvIGVzdGltYXRlIGl0Lg0KDQoNCmBgYHtyfQ0KI2xpYnJhcnkocGxvdGx5KQ0KDQojIERlZmluZSB0aGUgZWxsaXB0aWMgcGFyYWJvbG9pZCBmdW5jdGlvbjogeiA9ICh4XjIgLyBhXjIpICsgKHleMiAvIGJeMikNCiMgQ3JlYXRlIGEgZ3JpZCBvZiB4IGFuZCB5IHZhbHVlcw0KeCA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjAwKQ0KeSA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjAwKQ0KeiA8LSBvdXRlcih4LCB5LCBmdW5jdGlvbih4LCB5KSAoeF4yICArIHleMiApKQ0KDQojIyBzYW1wbGUgcG9pbnRzIG9uIHRoZSBzdXJmYWNlIHRvIGRlZmluZSBhIGRhdGEgc2V0DQp4LnNpbSA8LSBzYW1wbGUoeCkNCnkuc2ltIDwtIHNhbXBsZSh5KQ0Kei5zYW1wbGUgPC0gKHguc2ltXjIgKyB5LnNpbV4yKSArIHJub3JtKDIwMCwgMCwgMC41KQ0KDQojIFBsb3QgbWFyZ2lucw0KbSA8LSBsaXN0KCBsID0gNTAsIHIgPSA1MCwgYiA9IDEwMCwgdCA9IDEwMCwgcGFkID0gNCkNCg0KIyBDcmVhdGUgdGhlIDNEIHN1cmZhY2UgcGxvdA0KcGxvdF9seSh4ID0geCwgeSA9IHksIHogPSB6LCB0eXBlID0gInN1cmZhY2UiKSAlPiUgICMgdGhlIHN1cmZhY2UNCiAgYWRkX21hcmtlcnMoeCA9IHguc2ltLCAgICMgc2FtcGxlIHBvaW50cyBuZWFyIHRoZSBzdXJmYWNlDQogICAgICAgICAgICAgIHkgPSB5LnNpbSwgDQogICAgICAgICAgICAgIHogPSB6LnNhbXBsZSwgICANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KG1hcmdpbiA9IG0sIA0KICAgICAgICAgIHRpdGxlID0gIkVsbGlwdGljIFBhcmFib2xvaWQ6IEh5cG90aGVzaXplZCBNb2RlbCBmb3IgRGF0YSBHZW5lcmF0aW9uIiwNCiAgICAgICAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlgiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKQ0KICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgKQ0KYGBgDQoNCg0KSW4gcHJhY3RpY2UsIHdlIG9ubHkgb2JzZXJ2ZSBkYXRhIHBvaW50cyBmcm9tIGFuIHVua25vd24gc3VyZmFjZS4gV2UgdGhlcmVmb3JlIHJlbW92ZSB0aGUgaHlwb3RoZXRpY2FsIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdmlzdWFsaXplIGp1c3QgdGhlIGRhdGEgcG9pbnRzIHRvIHJldmVhbCB0aGUgZGF0YSBjbG91ZCdzIHN0cnVjdHVyZS4NCg0KDQpgYGB7cn0NCnBsb3RfbHkoKSAlPiUNCiAgYWRkX21hcmtlcnMoeCA9IHguc2ltLCB5ID0geS5zaW0sIHogPSB6LnNhbXBsZSwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCggbWFyZ2luID0gbSwNCiAgICAgICAgICAgdGl0bGUgPSAiU2FtcGxlIHBvaW50cyBmcm9tIHRoZSBlbGxpcHRpYyBwYXJhYm9sb2lkIiwNCiAgICAgICAgICAgc2NlbmUgPSBsaXN0KCB4YXhpcyA9IGxpc3QodGl0bGUgPSAiWCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJZIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKQ0KICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgKQ0KYGBgDQoNClRoZXJlIGlzIGEgbm9ubGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0YXJnZXQgdmFyaWFibGUgKCR6JCkgYW5kIGZlYXR1cmUgdmFyaWFibGVzICgkeF8xJCBhbmQgJHhfMiQpLiBJbiB0aGUgZm9sbG93aW5nIGZpZ3VyZXMsIHdlIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRocmVlIG1vZGVscyBmaXR0ZWQgdG8gdGhlIHNhbWUgZGF0YXNldDogYSBzaW5nbGUgcGVyY2VwdHJvbiwgYSBvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24sIGFuZCBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbi4gV2UgdGhlbiBwbG90IHRoZWlyIGNvcnJlc3BvbmRpbmcgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZXMgdG8gdmlzdWFsaXplIHRoZSBnb29kbmVzcyBvZiBmaXQuDQoNCg0KYGBge3J9DQojbGlicmFyeShuZXVyYWxuZXQpDQojbGlicmFyeShnZ3Bsb3QyKQ0KI2xpYnJhcnkocGxvdGx5KSAgIyBGb3IgM0QgdmlzdWFsaXphdGlvbg0KDQojIEdlbmVyYXRlIHRyYWluaW5nIGRhdGENCiNzZXQuc2VlZCgxMjMpDQojeCA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjApDQojeSA8LSBzZXEoLTIsIDIsIGxlbmd0aCA9MjApDQojel90cmFpbiA8LSB4X3RyYWluXjIgKyB5X3RyYWluXjIgIyArIHJub3JtKDUwMCwgc2QgPSAwLjEpICAjIEFkZGVkIG5vaXNlDQoNCiMjIFRoZSB3b3JraW5nIGRhdGEgc2V0OiB3ZSBjYWxsIGl0IHRoZSB0cmFpbmluZyBkYXRhIHNldA0KdHJhaW4uZGF0YSA8LSBkYXRhLmZyYW1lKHggPSB4LnNpbSwgeSA9IHkuc2ltLCB6ID0gei5zYW1wbGUpDQoNCiMjIyBubyBoaWRkZW4gbGF5ZXINCmhpZGRlbjAuMCA8LSBuZXVyYWxuZXQoeiB+IHggKyB5LCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgIHN0ZXBtYXggPSAxZTYpICAjIEluY3JlYXNlIGl0ZXJhdGlvbnMNCg0KDQoNCiMjIyBzaW5nbGUgaGlkZGVuIGxheWVyDQpoaWRkZW4xLjggPC0gbmV1cmFsbmV0KHogfiB4ICsgeSwgDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gYyg4KSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbmVhci5vdXRwdXQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBzdGVwbWF4ID0gMWU2KSAgIyBJbmNyZWFzZSBpdGVyYXRpb25zDQoNCg0KDQojIFRyYWluIG5ldXJhbCBuZXR3b3JrIHdpdGggMiBoaWRkZW4gbGF5ZXJzICg4IGFuZCA0IG5ldXJvbnMpDQpoaWRkZW4yLjguNCA8LSBuZXVyYWxuZXQoeiB+IHggKyB5LCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDgsNCksIA0KICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXIub3V0cHV0ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RlcG1heCA9IDFlNikgICMgSW5jcmVhc2UgaXRlcmF0aW9ucw0KDQojIENyZWF0ZSB0ZXN0IGdyaWQgdG8gZHJhdyB0aGUgUHJlZGljdGVkIHN1cmZhY2UgYmFzZWQgb24gZGlmZmVyZW50IE5OIG1vZGVscw0KeC50ZXN0IDwtIHNlcSgtMiwgMiwgbGVuZ3RoLm91dCA9IDIwMCkNCnkudGVzdCA8LSBzZXEoLTIsIDIsIGxlbmd0aC5vdXQgPSAyMDApDQp0ZXN0LmdyaWQgPC0gZXhwYW5kLmdyaWQoeCA9IHgudGVzdCwgeSA9IHkudGVzdCkNCnoudGVzdDAuMCA8LSBtYXRyaXgocHJlZGljdChoaWRkZW4wLjAsIHRlc3QuZ3JpZCksbmNvbD0yMDApDQp6LnRlc3QxLjggPC0gbWF0cml4KHByZWRpY3QoaGlkZGVuMS44LCB0ZXN0LmdyaWQpLG5jb2w9MjAwKQ0Kei50ZXN0Mi44LjQgPC0gbWF0cml4KHByZWRpY3QoaGlkZGVuMi44LjQsIHRlc3QuZ3JpZCksbmNvbD0yMDApDQoNCmBgYA0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgcHJlZGljdGVkIHN1cmZhY2UgZnJvbSB0aGUgcGVyY2VwdHJvbiBtb2RlbCwgd2hpY2ggcHJvZHVjZXMgYSBsaW5lYXIgaHlwZXJwbGFuZSwgYWxvbmcgd2l0aCB0aGUgZGF0YSBwb2ludHMgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcmVncmVzc2lvbiBzdXJmYWNlLiBUaGUgcGVyY2VwdHJvbiBmaXRzIHRoZSBkYXRhIHBvb3JseS4NCg0KDQpgYGB7cn0NCiMjIHNldCB1cCBwbG90IG1hcmdpbg0KbSA8LSBsaXN0KGwgPSA1MCxyID0gNTAsIGIgPSAxMDAsIHQgPSAxMDAsIHBhZCA9IDQpDQoNCiMgM0QgVmlzdWFsaXphdGlvbiB3aXRoIHBsb3RseQ0KcGxvdF9seSh4ID0gfngudGVzdCwgeSA9IH55LnRlc3QsIHogPSB+ei50ZXN0MC4wLCB0eXBlID0gInN1cmZhY2UiKSAlPiUNCiAgYWRkX21hcmtlcnMoZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56LCANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ1BlcmNlcHRyb24gTW9kZWwgKHdpdGggbm8gaGlkZGVuIGxheWVyKScsDQogICAgICAgICBhdXRvc2l6ZSA9IFQsIA0KICAgICAgICAgI3dpZHRoID0gNTAwLCANCiAgICAgICAgICNoZWlnaHQgPSA1MDAsDQogICAgICAgICBtYXJnaW4gPSBtLCANCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiWCIpLA0KICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlkiKSwNCiAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJaIikpKQ0KYGBgDQoNCg0KTmV4dCwgd2UgZml0IGEgKipvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24gd2l0aCA4IG5vZGVzKiogdG8gdGhlIHNhbWUgZGF0YXNldC4gVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgYm90aCB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZSBhbmQgdGhlIGRhdGEgcG9pbnRzLiBXZSBvYnNlcnZlIHRoYXQgdGhlIHByZWRpY3RlZCBzdXJmYWNlIGNsb3NlbHkgYXBwcm94aW1hdGVzIHRoZSAqKmh5cG90aGVzaXplZCByZWdyZXNzaW9uIHN1cmZhY2UqKi4gTm90ZSB0aGF0IHRoZSBudW1iZXIgb2Ygbm9kZXMgaW4gdGhlIGhpZGRlbiBsYXllciBpcyBhIHR1bmFibGUgaHlwZXJwYXJhbWV0ZXIgdGhhdCBjYW4gYmUgb3B0aW1pemVkIHRvIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UuDQoNCg0KYGBge3J9DQojIDNEIFZpc3VhbGl6YXRpb24gd2l0aCBwbG90bHkNCnBsb3RfbHkoeCA9IH54LnRlc3QsIHkgPSB+eS50ZXN0LCB6ID0gfnoudGVzdDEuOCwgdHlwZSA9ICJzdXJmYWNlIikgJT4lDQogIGFkZF9tYXJrZXJzKGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgeCA9IH54LCB5ID0gfnksIHogPSB+eiwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdFc3RpbWF0ZWQgT25lLWhpZGRlbi1sYXllciBQZXJjZXB0cm9uICh3aXRoIDggbm9kZXMpJywNCiAgICAgICAgIGF1dG9zaXplID0gVCwgDQogICAgICAgICAjd2lkdGggPSA1MDAsIA0KICAgICAgICAgI2hlaWdodCA9IDUwMCwNCiAgICAgICAgIG1hcmdpbiA9IG0sIA0KICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKSkpDQpgYGANCg0KRmluYWxseSwgd2UgZXhhbWluZSBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBhcmNoaXRlY3R1cmUsIHdpdGggOCBub2RlcyBpbiB0aGUgZmlyc3QgaGlkZGVuIGxheWVyIGFuZCA0IG5vZGVzIGluIHRoZSBzZWNvbmQgaGlkZGVuIGxheWVyLCB0cmFpbmVkIG9uIHRoZSBzYW1lIGRhdGEgc2V0Lg0KDQoNCmBgYHtyfQ0KIyAzRCBWaXN1YWxpemF0aW9uIHdpdGggcGxvdGx5DQpwbG90X2x5KHggPSB+eC50ZXN0LCB5ID0gfnkudGVzdCwgeiA9IH56LnRlc3QyLjguNCwgdHlwZSA9ICJzdXJmYWNlIikgJT4lDQogIGFkZF9tYXJrZXJzKGRhdGEgPSB0cmFpbi5kYXRhLCANCiAgICAgICAgICAgICAgeCA9IH54LCB5ID0gfnksIHogPSB+eiwgDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDMsIGNvbG9yID0gImRhcmtyZWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdFc3RpbWF0ZWQgVHdvLWhpZGRlbi1sYXllciBQZXJjZXB0cm9uICh3aXRoIDggYW5kIDQgbm9kZXMpJywNCiAgICAgICAgIGF1dG9zaXplID0gVCwgDQogICAgICAgICAjd2lkdGggPSA1MDAsIA0KICAgICAgICAgI2hlaWdodCA9IDUwMCwNCiAgICAgICAgIG1hcmdpbiA9IG0sIA0KICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKSkpDQpgYGANCg0KVGhlIGZpZ3VyZSBhYm92ZSBzaG93cyBib3RoIHRoZSBlc3RpbWF0ZWQgcHJlZGljdGlvbiBzdXJmYWNlIGFuZCB0aGUgb3JpZ2luYWwgZGF0YSBwb2ludHMuIFdlIG9ic2VydmUgYSBzaWduaWZpY2FudCBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSAqKmVzdGltYXRlZCByZWdyZXNzaW9uIHN1cmZhY2UqKiBhbmQgdGhlICoqaHlwb3RoZXNpemVkICh0cnVlKSByZWdyZXNzaW9uIHN1cmZhY2UqKi4gVXBvbiBjbG9zZXIgZXhhbWluYXRpb24sIHdlIGZpbmQgdGhhdCB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZSB5aWVsZHMgc21hbGwgcmVzaWR1YWxzLCBhcyB0aGUgZGF0YSBwb2ludHMgbGllIGNsb3NlIHRvIHRoZSBmaXR0ZWQgc3VyZmFjZS4gVGhpcyBzdWdnZXN0cyBwb3RlbnRpYWwgKipvdmVyZml0dGluZyoqIGluIHRoZSB0d28taGlkZGVuLWxheWVyIHBlcmNlcHRyb24gbW9kZWwuDQoNCg0KIyBNTFAgTW9kZWxpbmcgUHJvY2VzcyANCg0KTUxQcyBjYW4gbGVhcm4gY29tcGxleCwgbm9uLWxpbmVhciByZWxhdGlvbnNoaXBzIGR1ZSB0byB0aGVpciBtdWx0aXBsZSBsYXllcnMgb2YgaW50ZXJjb25uZWN0ZWQgbmV1cm9ucy4gSG93ZXZlciwgZGVzaWduaW5nIGFuZCB0cmFpbmluZyBhbiBlZmZlY3RpdmUgTUxQIHJlcXVpcmVzIGNhcmVmdWwgY29uc2lkZXJhdGlvbiBvZiBkYXRhIHByZXByb2Nlc3NpbmcsIGFyY2hpdGVjdHVyZSBzZWxlY3Rpb24sIG9wdGltaXphdGlvbiB0ZWNobmlxdWVzLCBhbmQgb3RoZXIgc3RyYXRlZ2llcy4NCg0KIyMgRGF0YSBQcmVwcm9jZXNzaW5nDQoNCkJlZm9yZSB0cmFpbmluZyBhbiBNTFAsIHRoZSBpbnB1dCBkYXRhIG11c3QgYmUgcHJvcGVybHkgcHJlcGFyZWQgdG8gbWFrZSBzdXJlIHRoZSBNTFAgbGVhcm5zIGVmZmljaWVudGx5Lg0KDQojIyMgRmVhdHVyZSBTY2FsaW5nDQoNCk1MUHMgYXJlIHNlbnNpdGl2ZSB0byBmZWF0dXJlIHNjYWxlcywgc28gbm9ybWFsaXphdGlvbiAoZS5nLiwgTWluLU1heCBzY2FsaW5nKSBvciBzdGFuZGFyZGl6YXRpb24gKGUuZy4sIFotc2NvcmUgbm9ybWFsaXphdGlvbikgaXMgdHlwaWNhbGx5IGFwcGxpZWQgdG8gZW5zdXJlIHN0YWJsZSBncmFkaWVudCBkZXNjZW50IG9wdGltaXphdGlvbi4gDQoNCldoZXRoZXIgdGhlIHRhcmdldCB2YXJpYWJsZSBtdXN0IGJlIHJlc2NhbGVkIGluIGEgbmV1cmFsIG5ldHdvcmsgZGVwZW5kcyBvbiB0aGUgY29udGV4dCwgYnV0IGl0IGlzIG9mdGVuIHN0cm9uZ2x5IHJlY29tbWVuZGVkIGZvciBvcHRpbWFsIHBlcmZvcm1hbmNlLg0KDQpUaGUgc2NhbGluZyBvZiB0aGUgdGFyZ2V0IGlzIG5lY2Vzc2FyeSB3aGVuDQoNCiogKipPdXRwdXQgQWN0aXZhdGlvbiBDb25zdHJhaW50cyoqOiBJZiB1c2luZyBhIHNpZ21vaWQgKGJvdW5kZWQgdG8gWzAsIDFdKSBvciB0YW5oIChib3VuZGVkIHRvIFstMSwgMV0pIG91dHB1dCBhY3RpdmF0aW9uLCB0aGUgdGFyZ2V0IHZhcmlhYmxlIG11c3QgYmUgc2NhbGVkIHRvIG1hdGNoIHRoZXNlIHJhbmdlcy4NCg0KKiAqKkxvc3MgRnVuY3Rpb24gU2Vuc2l0aXZpdHkqKjogTG9zcyBmdW5jdGlvbnMgbGlrZSBNU0UgKE1lYW4gU3F1YXJlZCBFcnJvcikgYXJlIHNlbnNpdGl2ZSB0byBzY2FsZS4gTGFyZ2UgdGFyZ2V0IHZhbHVlcyBjYW4gbGVhZCB0byB1bnN0YWJsZSBncmFkaWVudHMgb3Igc2xvdyBjb252ZXJnZW5jZS4NCg0KKiAqKlJlZ3VsYXJpemF0aW9uIFRlcm1zKio6ICRMXzEkLyRMXzIkIHJlZ3VsYXJpemF0aW9uIHBlbmFsaXplcyBsYXJnZSB3ZWlnaHRzLiBVbnNjYWxlZCB0YXJnZXRzIG1heSBmb3JjZSB0aGUgbmV0d29yayB0byBsZWFybiBkaXNwcm9wb3J0aW9uYXRlbHkgbGFyZ2Ugd2VpZ2h0cyB0byBjb21wZW5zYXRlLg0KDQpXaGlsZSBub3QgYWx3YXlzIG1hbmRhdG9yeSwgKipyZXNjYWxpbmcgdGhlIHRhcmdldCB2YXJpYWJsZSoqIHR5cGljYWxseSAqKmltcHJvdmVzKiogdHJhaW5pbmcgc3RhYmlsaXR5LCBzcGVlZCwgYW5kIG1vZGVsIHBlcmZvcm1hbmNl4oCUZXNwZWNpYWxseSBmb3IgYm91bmRlZCBhY3RpdmF0aW9ucyBvciBsYXJnZS12YWx1ZSByYW5nZXMuIEFsd2F5cyB2YWxpZGF0ZSB3aXRoIHlvdXIgc3BlY2lmaWMgYXJjaGl0ZWN0dXJlIGFuZCBkYXRhLg0KDQoNCg0KIyMjIEhhbmRsaW5nIE1pc3NpbmcgRGF0YQ0KDQpIYW5kbGluZyBtaXNzaW5nIHZhbHVlcyBpbiAqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiogcmVxdWlyZXMgY2FyZWZ1bCBjb25zaWRlcmF0aW9uLCBhcyBuZXVyYWwgbmV0d29ya3MgdHlwaWNhbGx5IGV4cGVjdCBjb21wbGV0ZSBpbnB1dCBkYXRhLg0KDQpBbGwgaW1wdXRhdGlvbiBtZXRob2RzIGRpc2N1c3NlZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiBjYW4gYmUgdXNlZCB0byBwcmVwcm9jZXNzIGRhdGEgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgTUxQIG1vZGVsaW5nLiBUaGUgZ2VuZXJhbCByZWNvbW1lbmRhdGlvbiBpcyB0byB1c2Ugc3RvY2hhc3RpYyBtdWx0aXBsZSBpbXB1dGF0aW9uIHRlY2huaXF1ZXMsIHN1Y2ggYXMgTUlDRSAoTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucykgYW5kIG90aGVyIHJhbmRvbSByZXBsYWNlbWVudCBtZXRob2RzIHRoYXQgbWFrZSB1c2Ugb2YgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgdW5kZXJseWluZyBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KDQojIyMgQ2F0ZWdvcmljYWwgRW5jb2RpbmcNCg0KKipDYXRlZ29yaWNhbCBlbmNvZGluZyoqIHRyYW5zZm9ybXMgbm9uLW51bWVyaWMgdmFyaWFibGVzIGludG8gbnVtZXJpY2FsIHJlcHJlc2VudGF0aW9ucyBmb3IgbWFjaGluZSBsZWFybmluZy4gDQoNCiogRm9yICoqbm9taW5hbCBkYXRhIChubyBpbmhlcmVudCBvcmRlcikqKiwgY29tbW9uIG1ldGhvZHMgaW5jbHVkZSBvbmUtaG90IGVuY29kaW5nLCB3aGljaCBjcmVhdGVzIGJpbmFyeSBjb2x1bW5zIGZvciBlYWNoIGNhdGVnb3J5IChpZGVhbCBmb3IgPDE1IGNhdGVnb3JpZXMpLCBhbmQgdGFyZ2V0IGVuY29kaW5nLCB3aGljaCByZXBsYWNlcyBjYXRlZ29yaWVzIHdpdGggdGhlIG1lYW4gb2YgdGhlIHRhcmdldCB2YXJpYWJsZSAoYmV0dGVyIGZvciBoaWdoLWNhcmRpbmFsaXR5IGZlYXR1cmVzIGJ1dCByZXF1aXJlcyBjYXJlZnVsIHZhbGlkYXRpb24gdG8gYXZvaWQgbGVha2FnZSkuIA0KDQoqIEZvciAqKm9yZGluYWwgZGF0YSAobmF0dXJhbCBvcmRlcikqKiwgb3JkaW5hbCBlbmNvZGluZyBhc3NpZ25zIGludGVnZXJzIHdoaWxlIHByZXNlcnZpbmcgdGhlIGxvZ2ljYWwgc2VxdWVuY2UgKGUuZy4sIFNtYWxsPTEsIE1lZGl1bT0yKS4gDQoNCiogKipIaWdoLWNhcmRpbmFsaXR5IGZlYXR1cmVzKiogKGUuZy4sIFpJUCBjb2RlcykgbWF5IHVzZSBmcmVxdWVuY3kgZW5jb2RpbmcgKHJlcGxhY2luZyBjYXRlZ29yaWVzIHdpdGggdGhlaXIgb2NjdXJyZW5jZSBjb3VudHMpIG9yIGVtYmVkZGluZyBsYXllcnMgaW4gbmV1cmFsIG5ldHdvcmtzIHRvIGNhcHR1cmUgY29tcGxleCByZWxhdGlvbnNoaXBzLiANCg0KDQpUaGUgY2hvaWNlIGRlcGVuZHMgb24gdGhlIG1vZGVsIHR5cGUgYW5kIGRhdGEgY2hhcmFjdGVyaXN0aWNzOiANCg0KKiAqKlRyZWUtYmFzZWQgbW9kZWxzKiogKGUuZy4sIFJhbmRvbSBGb3Jlc3RzKSBvZnRlbiBoYW5kbGUgbGFiZWwgZW5jb2Rpbmcgd2VsbC4NCg0KKiAqKkxpbmVhciBtb2RlbHMgYW5kIG5ldXJhbCBuZXR3b3JrcyoqIHR5cGljYWxseSByZXF1aXJlIG9uZS1ob3Qgb3IgdGFyZ2V0IGVuY29kaW5nLiANCg0KQ3Jvc3MtdmFsaWRhdGlvbiBpcyBlc3NlbnRpYWwgdG8gZXZhbHVhdGUgdGhlIGltcGFjdCBvZiBlbmNvZGluZyBzdHJhdGVnaWVzIG9uIG1vZGVsIHBlcmZvcm1hbmNlLg0KDQoNCg0KIyMjIERhdGEgU3BsaXR0aW5nDQoNClByb3BlciBkYXRhIHNwbGl0dGluZyBpcyBjcnVjaWFsIGZvciB0cmFpbmluZyAqKk11bHRpbGF5ZXIgUGVyY2VwdHJvbnMgKE1MUHMpKiogdG8gZW5zdXJlIHJvYnVzdCBtb2RlbCBldmFsdWF0aW9uIGFuZCBwcmV2ZW50IG92ZXJmaXR0aW5nLiBGb3IgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEsIGEgcmFuZG9tIHNwbGl0dGluZyBpcyByZXF1aXJlZDoNCg0KKiBUd28td2F5IHNwbGl0dGluZyBmb3IgdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YSBzZXRzLiBUaGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSByYXRpbyBpcyB1c3VhbGx5IDcwJS0zMCUgb3IgODAlLTIwJS4NCg0KKiAqKk5lc3RlZCByYW5kb20gc3BsaXR0aW5nIGZvciBjcm9zcy12YWxpZGF0aW9uKiogaW52b2x2ZXMgdHdvIGxheWVycyBvZiBkYXRhIHBhcnRpdGlvbmluZzogYW4gb3V0ZXIgbG9vcCBmb3IgcGVyZm9ybWFuY2UgZXZhbHVhdGlvbiBhbmQgYW4gaW5uZXIgbG9vcCBmb3IgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nLiBUaGlzIGFwcHJvYWNoIGVuc3VyZXMgdW5iaWFzZWQgbW9kZWwgYXNzZXNzbWVudCBieSBwcmV2ZW50aW5nIGRhdGEgbGVha2FnZSBiZXR3ZWVuIHRoZSB0dW5pbmcgYW5kIGV2YWx1YXRpb24gcGhhc2VzLg0KDQpUaGVyZSBhcmUgb3RoZXIgcmFuZG9tIGRhdGEgc3BsaXR0aW5nIG1ldGhvZHMgYmFzZWQgb24gdGhlIGRhdGEgc3RydWN0dXJlcyB0aGF0IHJlcXVpcmUgc3BlY2lhbCBkZXNpZ24gc28gdGhhdCB0aGUgcmFuZG9tbHkgc3BsaXQgZGF0YSByZXRhaW4gdGhlIHNhbWUgcHJvYmFiaWxpdHkgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHNldC4NCg0KDQojIyBNTFAgQXJjaGl0ZWN0dXJlIERlc2luZw0KDQpJbiBuZXVyYWwgbmV0d29ya3MsIE1MUCAoTXVsdGlsYXllciBQZXJjZXB0cm9uKSBzaXplIGFuZCBkZXB0aCByZWZlciB0byBpdHMgYXJjaGl0ZWN0dXJlLCBwYXJ0aWN1bGFybHkgdGhlIG51bWJlciBvZiBuZXVyb25zIChzaXplKSBhbmQgdGhlIG51bWJlciBvZiBoaWRkZW4gbGF5ZXJzIChkZXB0aCkuIFRoZXNlIGZhY3RvcnMgc2lnbmlmaWNhbnRseSBpbXBhY3QgdGhlIG1vZGVsJ3MgY2FwYWNpdHksIHRyYWluaW5nIGR5bmFtaWNzLCBhbmQgZ2VuZXJhbGl6YXRpb24gYWJpbGl0eS4NCg0KKiAqKlNoYWxsb3cgbmV0d29ya3MqKiAoMS0yIGhpZGRlbiBsYXllcnMpIG1heSBzdWZmaWNlIGZvciBzaW1wbGUgdGFza3MuICAqKlNoYWxsb3cgbmV0d29ya3MqKiANCiAgKyBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24gKFVuaXZlcnNhbCBBcHByb3hpbWF0aW9uIFRoZW9yZW0pLg0KICArIG9mdGVuIHN1ZmZpY2llbnQgZm9yIHNpbXBsZSB0YXNrcyAoZS5nLiwgc21hbGwgdGFidWxhciBkYXRhc2V0cykuDQogICsgYXJlIGxlc3MgcHJvbmUgdG8gb3ZlcmZpdHRpbmcgYnV0IG1heSB1bmRlcmZpdCBjb21wbGV4IGRhdGEuDQoNCiogKipEZWVwIG5ldHdvcmtzKiogY2FuIG1vZGVsIGNvbXBsZXggcGF0dGVybnMgYnV0IHJpc2sgb3ZlcmZpdHRpbmcgYW5kIGluY3JlYXNlZCBjb21wdXRhdGlvbmFsIGNvc3QuICoqRGVlcCBuZXR3b3JrcyoqIGFyZQ0KICArIGJldHRlciBhdCBsZWFybmluZyBoaWVyYXJjaGljYWwgcmVwcmVzZW50YXRpb25zLg0KICArIHVzZWZ1bCBmb3IgaGlnaC1kaW1lbnNpb25hbCBkYXRhIChlLmcuLCBpbWFnZXMsIE5MUCBhZnRlciBmZWF0dXJlIGV4dHJhY3Rpb24pLg0KICArIG1vcmUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSBhbmQgcHJvbmUgdG8gb3ZlcmZpdHRpbmcgKHJlcXVpcmVzIHJlZ3VsYXJpemF0aW9uIGxpa2UgRHJvcG91dCwgTDIpLg0KDQoNCiogKipNTFAgU2l6ZSAoTnVtYmVyIG9mIE5ldXJvbnMgcGVyIExheWVyKSoqIGlzIGFsc28gaW1wYWN0ZnVsIG9uIHRoZSBtb2RlbCBwZXJmb3JtYW5jZS4NCiAgKyBXaWRlciBMYXllcnMgKG1vcmUgbmV1cm9ucykgaW5jcmVhc2UgbW9kZWwgY2FwYWNpdHksIGFsbG93aW5nIG1vcmUgY29tcGxleCBmdW5jdGlvbiBhcHByb3hpbWF0aW9uLiBJdCBjYW4gbGVhZCB0byBvdmVyZml0dGluZyBpZiBub3QgcmVndWxhcml6ZWQgYW5kIHVzZSBtb3JlIGNvbXB1dGF0aW9uIHJlc291cmNlcyAobW9yZSBwYXJhbWV0ZXJzKS4NCiAgKyBOYXJyb3dlciBMYXllcnMgKGZld2VyIG5ldXJvbnMpIGFyZSBsZXNzIGV4cHJlc3NpdmUgYnV0IGZhc3RlciB0byB0cmFpbiBidXQgbWF5IGxlYWQgdG8gdW5kZXJmaXR0aW5nIGNvbXBsZXggcGF0dGVybnMuDQoNCiogKipDaG9vc2luZyBEZXB0aCAmIFNpemUqKiBpcyBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIGdlbmVyYWwgcmVjb21tZW5kYXRpb25zDQogICsgU3RhcnQgd2l0aCAxLTMgaGlkZGVuIGxheWVycyBhbmQgYWRqdXN0IGJhc2VkIG9uIHBlcmZvcm1hbmNlLg0KICArIFVzZSBzaW1pbGFyIG9yIGRlY3JlYXNpbmcgbGF5ZXIgc2l6ZXMuDQogICsgV2lkZXIgbGF5ZXJzIGVhcmx5IGNhbiBoZWxwIGZlYXR1cmUgZXh0cmFjdGlvbjsgZGVlcGVyIG5ldHdvcmtzIHJlZmluZSBhYnN0cmFjdGlvbnMuDQoNCg0KIyMgQWN0aXZhdGlvbiBGdW5jdGlvbnMNCg0KKipBY3RpdmF0aW9uIGZ1bmN0aW9ucyoqIHNlcnZlIGRpc3RpbmN0IHB1cnBvc2VzIGluIGhpZGRlbiBhbmQgb3V0cHV0IGxheWVycy4gSW4gaGlkZGVuIGxheWVycywgdGhleSBpbnRyb2R1Y2Ugbm9ubGluZWFyIHRyYW5zZm9ybWF0aW9ucyB0byBjYXB0dXJlIGNvbXBsZXggcGF0dGVybnMgaW4gdGhlIGRhdGEuIFRoZSBvdXRwdXQgbGF5ZXIncyBhY3RpdmF0aW9uIGZ1bmN0aW9uIGlzIGNob3NlbiBzcGVjaWZpY2FsbHkgdG8gcHJvZHVjZSBwcmVkaWN0aW9ucyBjb21wYXRpYmxlIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSdzIGNoYXJhY3RlcmlzdGljcy4gDQoNCg0KIyMjIEhpZGRlbiBMYXllciBBY3RpdmF0aW9uDQoNClRoZXJlIGFyZSBzZXZlcmFsIGFjdGl2YXRpb24gZnVuY3Rpb25zIGZvciB0aGUgaGlkZGVuIGxheWVyLiBUaGUgUmVMVSAoUmVjdGlmaWVkIExpbmVhciBVbml0KSBpcyB3aWRlbHkgdXNlZCBkdWUgdG8gaXRzIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeSBhbmQgaXMgdGhlIGRlZmF1bHQgZm9yIG1hbnkgTUxQIGxpYnJhcmllcy4gVGhlIGZvbGxvd2luZyB0YWJsZSBvdXRsaW5lcyBzb21lIG9mIHRoZSBjb21tb25seSB1c2VkIGFjdGl2YXRpb24gZnVuY3Rpb25zIGZvciBoaWRkZW4gbGF5ZXJzLg0KDQoNCnwgQWN0aXZhdGlvbgl8IEJlc3QgRm9yCSB8ICAgIFByb3MJICB8ICAgIENvbnMgCXwgUmVjb21tZW5kYXRpb24gfA0KfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLXw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tfA0KfCBSZUxVCXwgRGVmYXVsdCBjaG9pY2UJfCBGYXN0LCBhdm9pZHMgdmFuaXNoaW5nIGdyYWRpZW50IChmb3IgeCA+IDApCXwgIkR5aW5nIFJlTFUiIChkZWFkIG5ldXJvbnMpCXwgRmlyc3QgdHJ5IGluIG1vc3QgTUxQc3wgDQp8IExlYWt5IFJlTFUJfCBEZWVwIG5ldHdvcmtzCXwgRml4ZXMgZHlpbmcgUmVMVQl8IFNsaWdodGx5IHNsb3dlciB0aGFuIFJlTFUJfCBVc2UgaWYgUmVMVSBmYWlsc3wgDQp8IEdFTFUJfCBUcmFuc2Zvcm1lcnMsIGRlZXAgbGVhcm5pbmcJfCBTbW9vdGgsIGJldHRlciBncmFkaWVudCBmbG93CXwgTW9yZSBjb21wdXRlLWhlYXZ5CXwgIEJlc3QgZm9yIG1vZGVybiBkZWVwIG5ldHMgKGUuZy4sIEJFUlQsIEdQVCl8IA0KfCBTd2lzaAl8IEV4cGVyaW1lbnRhbCBhbHRlcm5hdGl2ZQl8IENhbiBvdXRwZXJmb3JtIFJlTFUJfCBTbG93ZXIgdGhhbiBSZUxVCXwgIFRyeSBpZiB0dW5pbmcgcGVyZm9ybWFuY2V8IA0KfCBTRUxVCXwgU2VsZi1ub3JtYWxpemluZyBuZXR3b3Jrcwl8IE5vIEJhdGNoTm9ybSBuZWVkZWQJfCBSZXF1aXJlcyBjYXJlZnVsIGluaXRpYWxpemF0aW9uCXwgVXNlIG9ubHkgaW4gc3BlY2lmaWMgYXJjaGl0ZWN0dXJlc3wgDQoNCg0KPGZvbnQgY29sb3IgPSAicmVkIj4qKlxjb2xvcntyZWR9QXZvaWQgdXNpbmcgU2lnbW9pZC9UYW5oIGluIGhpZGRlbiBsYXllcnMgdGhhdCBjYXVzZSB2YW5pc2hpbmcgZ3JhZGllbnRzIGluIGRlZXAgbmV0d29ya3MuKio8L2ZvbnQ+DQoNCg0KDQojIyMgT3V0cHV0IExheWVyIEFjdGl2YXRpb24NCg0KRGVwZW5kaW5nIG9uIHRoZSB0eXBlIG9mIHRoZSB0YXJnZXQgcmVzcG9uc2UsIHRoZSBjb21tb25seSB1c2VkIGFjdGl2YXRpb24gZnVuY3Rpb25zIGFyZSBzdW1tYXJpemVkIGluIHRoZSBmb2xsb3dpbmcgdGFibGUuDQoNCnwgIFRhc2sJICAgICB8ICAgIEFjdGl2YXRpb24JICB8ICAgIFdoeT8gICAJfCAgICAgIEV4YW1wbGUgVXNlIENhc2UgICAgIHwNCnw6LS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAgUmVncmVzc2lvbiAodW5ib3VuZGVkKQl8ICBMaW5lYXIgKG5vIGFjdGl2YXRpb24pCXwgIE91dHB1dHMgYW55IHJlYWwgbnVtYmVyCXwgIFByZWRpY3RpbmcgaG91c2UgcHJpY2VzfCAgDQp8ICBSZWdyZXNzaW9uICgwIHRvIDEpCXwgIFNpZ21vaWQJfCAgQm91bmRzIG91dHB1dCB0byBbMCwgMV0JfCAgUHJlZGljdGluZyBwcm9iYWJpbGl0aWVzfCAgDQp8ICBCaW5hcnkgQ2xhc3NpZmljYXRpb24JfCAgU2lnbW9pZAl8ICBPdXRwdXRzIHByb2JhYmlsaXR5ICgwIG9yIDEpCXwgIFNwYW0gZGV0ZWN0aW9ufCAgDQp8ICBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uCXwgIFNvZnRtYXgJfCAgUHJvYmFiaWxpdGllcyBzdW0gdG8gMQl8ICBNTklTVCBkaWdpdCBjbGFzc2lmaWNhdGlvbnwgIA0KfCAgTXVsdGlsYWJlbCBDbGFzc2lmaWNhdGlvbgl8ICBTaWdtb2lkIChwZXIgY2xhc3MpCXwgIEluZGVwZW5kZW50IHByb2JhYmlsaXRpZXMJfCAgSW1hZ2UgdGFnZ2luZ3wgIA0KDQoNCjxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfU5ldmVyIHVzZSBSZUxVIGluIHRoZSBvdXRwdXQgbGF5ZXIgdW5sZXNzIG91dHB1dHMgbXVzdCBiZSDiiaUwLCBlLmcuLCBjb3VudCBwcmVkaWN0aW9uICBsaWtlIFBvaXNzb24gcmVncmVzc2lvbikuKio8L2ZvbnQ+DQoNCg0KIyMjIEd1aWRlbGluZSBmb3IgSGlkZGVuIGFuZCBPdXRwdXQgTGF5ZXJzIA0KDQoNClRoZSBmb2xsb3dpbmcgdGFibGUgcHJvdmlkZXMgYSBndWlkZWxpbmUgZm9yIGNob29zaW5nIGFwcHJvcHJpYXRlIGFjdGl2YXRpb24gZnVuY3Rpb25zIGluIE1MUC4NCg0KfCAgU2NlbmFyaW8gICB8IAlIaWRkZW4gTGF5ZXIgIHwgCU91dHB1dCBMYXllciAgfA0KfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS18DQp8ICBHZW5lcmFsIE1MUAl8ICBSZUxVCXwgIFRhc2stZGVwZW5kZW50IChzZWUgYWJvdmUpfCAgDQp8ICBEZWVwIE5ldHdvcmsJfCAgR0VMVS9MZWFreSBSZUxVIHwgIAlUYXNrLWRlcGVuZGVudHwgIA0KfCAgVHJhbnNmb3JtZXIgTW9kZWwJfCAgR0VMVQl8ICBTb2Z0bWF4L1NpZ21vaWR8ICANCnwgIFNlbGYtTm9ybWFsaXppbmcgTmV0CXwgIFNFTFUJfCAgVGFzay1kZXBlbmRlbnR8ICANCg0KDQojIyBUcmFpbmluZywgT3B0aW1pemF0aW9uIA0KDQpFZmZlY3RpdmUgdHJhaW5pbmcgcmVxdWlyZXMgcHJvcGVyIG9wdGltaXphdGlvbiB0ZWNobmlxdWVzIGFuZCBoeXBlcnBhcmFtZXRlciB0dW5pbmcuDQoNCiogKipMb3NzIEZ1bmN0aW9uIFNlbGVjdGlvbioqIGlzIGRlcGVuZGVudCBvbiB0aGUgYXBwbGljYXRpb25zOg0KICArICoqQ2xhc3NpZmljYXRpb24qKjogQ3Jvc3MtZW50cm9weSBsb3NzDQogICsgKipSZWdyZXNzaW9uKio6IE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBvciBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpDQoNCiogKipPcHRpbWl6ZXIgQ2hvaWNlKiogaXMgYWxzbyBkZXBlbmRlbnQgb24gdGhlIGFwcGxpY2F0aW9ucyBhbmQgdGhlIHJlcXVpcmVkIGNvbXB1dGF0aW9uYWwgcmVzb3VyY2VzIGZvciB0aGUgYXBwbGljYXRpb25zLg0KICArIFN0b2NoYXN0aWMgR3JhZGllbnQgRGVzY2VudCAoU0dEKTogQmFzaWMgYnV0IHJlcXVpcmVzIGNhcmVmdWwgbGVhcm5pbmcgcmF0ZSB0dW5pbmcuIA0KICArIEFkYXB0aXZlIE9wdGltaXplcnMgKEFkYW0sIFJNU3Byb3ApOiBBdXRvbWF0aWNhbGx5IGFkanVzdCBsZWFybmluZyByYXRlcywgb2Z0ZW4gbGVhZGluZyB0byBmYXN0ZXIgY29udmVyZ2VuY2UuIEEgdG9vLWhpZ2ggbGVhcm5pbmcgcmF0ZSBjYXVzZXMgaW5zdGFiaWxpdHksIHdoaWxlIGEgdG9vLWxvdyByYXRlIHNsb3dzIGNvbnZlcmdlbmNlLg0KDQoqICoqSHlwZXJwYXJhbWV0ZXIgVHVuaW5nKioNCk1ldGhvZHMgbGlrZSBncmlkIHNlYXJjaCwgcmFuZG9tIHNlYXJjaCwgb3IgQmF5ZXNpYW4gb3B0aW1pemF0aW9uIGhlbHAgZmluZCBvcHRpbWFsIGNvbmZpZ3VyYXRpb25zIGVmZmljaWVudGx5Lg0KDQpcDQoNCiMjIFJlbGF0aXZlIEltcHJvdmVtZW50IGluIFBlcmZvcm1hbmNlDQoNCldoZW4gZXZhbHVhdGluZyBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscywgaXQgaXMgZXNzZW50aWFsIHRvIHF1YW50aWZ5IGhvdyBtdWNoIGEgcHJvcG9zZWQgbmV1cmFsIG5ldHdvcmsgKE5OKSBtb2RlbCBpbXByb3ZlcyB1cG9uIGEgc2ltcGxlciBiYXNlIG1vZGVsIChlLmcuLCBsaW5lYXIgcmVncmVzc2lvbiBmb3IgcmVncmVzc2lvbiB0YXNrcywgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIGNsYXNzaWZpY2F0aW9uKS4gTWVhc3VyaW5nIHJlbGF0aXZlIGltcHJvdmVtZW50IGhlbHBzIGRldGVybWluZSB3aGV0aGVyIHRoZSBhZGRlZCBjb21wbGV4aXR5IG9mIGEgbmV1cmFsIG5ldHdvcmsganVzdGlmaWVzIGl0cyBhZG9wdGlvbi4gV2UgbmV4dCBicmllZmx5IGRpc2N1c3Mga2V5IG1ldGhvZHMgZm9yIGNvbXB1dGluZyBhbmQgaW50ZXJwcmV0aW5nIHJlbGF0aXZlIGltcHJvdmVtZW50LCBhbG9uZyB3aXRoIHByYWN0aWNhbCBjb25zaWRlcmF0aW9ucy4gUmVjYWxsIHRoYXQgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24gYXJlIGxpc3RlZC4NCg0KICArICoqQ2xhc3NpZmljYXRpb24qKjogQWNjdXJhY3ksIFByZWNpc2lvbiwgUmVjYWxsLCBGMS1zY29yZSwgUk9DLUFVQy4NCiAgKyAqKlJlZ3Jlc3Npb24qKjogTVNFLCBSTVNFLCAkUl4yJC4NCiAgDQoqKlJlbGF0aXZlIGltcHJvdmVtZW50KiogbWVhc3VyZXMgaG93IG11Y2ggYmV0dGVyIChvciB3b3JzZSkgYSBuZXVyYWwgbmV0d29yayBwZXJmb3JtcyBjb21wYXJlZCB0byBhIGJhc2VsaW5lIG1vZGVsLiBJdCBpcyB0eXBpY2FsbHkgZXhwcmVzc2VkIGFzIGEgcGVyY2VudGFnZSByZWR1Y3Rpb24gaW4gZXJyb3IgKGZvciByZWdyZXNzaW9uKSBvciBhIHBlcmNlbnRhZ2UgaW5jcmVhc2UgaW4gYWNjdXJhY3kvcHJlY2lzaW9uL3JlY2FsbCAoZm9yIGNsYXNzaWZpY2F0aW9uKS4gIFRoZSBnZW5lcmFsIGZvcm11bGEgaXMgZ2l2ZW4gYnkNCg0KJCQNClx0ZXh0e1JlbGF0aXZlIEltcHJvdmVtZW50fSA9IFxmcmFje1x0ZXh0e01ldHJpY31fe1x0ZXh0e0Jhc2V9fS1cdGV4dHtNZXRyaWN9X3tcdGV4dHtOTn19fXtcdGV4dHtNZXRyaWN9X3tcdGV4dHtCYXNlfX19DQokJA0KICANCkFzIGFuIGV4YW1wbGUsIGNvbXBhcmUgdGhlIFJNU0UgYmV0d2VlbiBsaW5lYXIgcmVncmVzc2lvbiAoZml0IGl0IHRvIHRoZSBzY2FsZWQgZGF0YSkgYW5kIE5OIG1vZGVsLiBBc3N1bWUgdGhlIGJhc2UgbW9kZWwgUk1TRSA9IDEwLjAgYW5kIE5OIG1vZGVsIFJNU0UgPSA3LiBUaGVuDQoNCiQkDQpcdGV4dHtJbXByb3ZlbWVudH0gPSBcZnJhY3sxMC03fXsxMH0gPSAzMFwlLg0KJCQNCg0KKipJbnRlcnByZXRhdGlvbioqOiBUaGUgTk4gcmVkdWNlcyBwcmVkaWN0aW9uIGVycm9yIGJ5IDMwJSBjb21wYXJlZCB0byBsaW5lYXIgcmVncmVzc2lvbi4gIA0KICANCiAgDQpcDQogIA0KIyBNTFAgUmVncmVzc2lvbg0KDQpUaGlzIHNlY3Rpb24gZm9jdXNlcyBvbiBpbXBsZW1lbnRpbmcgTUxQIHJlZ3Jlc3Npb24gdXNpbmcgdGhlIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQsIGFsb25nIHdpdGggdGhlIFIgKipuZXVyYWxuZXQqKiBsaWJyYXJ5IGFuZCBvdGhlciBzdXBwb3J0aW5nIGxpYnJhcmllcyBmb3IgZGF0YSBwcmVwYXJhdGlvbiBhbmQgdmlzdWFsaXphdGlvbi4gV2Ugd2lsbCBmb2xsb3cgdGhlIGJhc2ljIHN0ZXBzIG91dGxpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLiBXZSB3aWxsIGZpdCBvbmUtaGlkZGVuLWxheWVyIGFuZCB0d28taGlkZGVuLWxheWVyIE1MUCB0byBwcmVkaWN0IHRoZSBtZWRpYW4gaG91c2UgdmFsdWUuDQoNCldlIHVzZSAqKm1pbi1tYXggc2NhbGluZyBtZXRob2QqKiBmb3IgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMuIEFsbCBmZWF0dXJlcyBhcmUgbnVtZXJpY2FsLCBubyBjYXRlZ29yaWNhbCBlbmNvZGluZyBpcyBuZWVkZWQuIFdlIHVzZSByYW5kb20gc3BsaXR0aW5nICg3MC0zMCkgdG8gY3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEgc2V0cy4NCg0KYGBge3J9DQojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcw0KIyBsaWJyYXJ5KG5ldXJhbG5ldCkNCiMgbGlicmFyeShNQVNTKSAgICAgICAjIEZvciBCb3N0b24gSG91c2luZyBkYXRhc2V0DQojIGxpYnJhcnkoZ2dwbG90MikgICAgIyBGb3IgdmlzdWFsaXphdGlvbg0KIyBsaWJyYXJ5KGNhcmV0KSAgICAgICMgT25seSBmb3IgZGF0YSBzcGxpdHRpbmcgKHdlIHdvbid0IHVzZSBpdHMgbW9kZWxpbmcgZnVuY3Rpb25zKQ0KIyBMb2FkIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQNCmRhdGEoQm9zdG9uKQ0KDQojIENoZWNrIHN0cnVjdHVyZSBhbmQgc3VtbWFyeQ0KI3N0cihCb3N0b24pDQojc3VtbWFyeShCb3N0b24pDQoNCiMgRmVhdHVyZSBzY2FsaW5nIC0gbm9ybWFsaXplIGFsbCB2YXJpYWJsZXMgdG8gWzAsMV0gcmFuZ2UNCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7DQogIHJldHVybiAoKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQoNCmJvc3Rvbi5zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoQm9zdG9uLCBub3JtYWxpemUpKQ0KDQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMykNCk4gPC0gbGVuZ3RoKGJvc3Rvbi5zY2FsZWQkbWVkdikNCiMgQ3JlYXRlIHRyYWluLXRlc3Qgc3BsaXQgKDcwLTMwKQ0KdHJhaW4ucmVnLmluZGV4IDwtIHNhbXBsZSgxOk4sICBmbG9vcigwLjcqTiksIHJlcGxhY2UgPSBGQUxTRSkNCnRyYWluLnJlZy5kYXRhIDwtIGJvc3Rvbi5zY2FsZWRbdHJhaW4ucmVnLmluZGV4LCBdDQp0ZXN0LnJlZy5kYXRhIDwtIGJvc3Rvbi5zY2FsZWRbLXRyYWluLnJlZy5pbmRleCwgXQ0KDQojIENoZWNrIGRpbWVuc2lvbnMNCiNkaW0odHJhaW5fZGF0YSkNCiNkaW0odGVzdF9kYXRhKQ0KYGBgDQoNCiMjIE9uZS1oaWRkZW4tbGF5ZXIgUGVyY2VwdHJvbg0KDQpXZSBmaXJzdCBidWlsZCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4gV2Ugd2lsbCB0dW5lIHRocmVlIGh5cGVycGFyYW1ldGVyczogdGhlIG51bWJlciBvZiBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyLCBsZWFybmluZyByYXRlLCBhbmQgYWN0aXZhdGlvbiBmdW5jdGlvbiB1c2luZyBncmlkIHNlYXJjaC4gVGhlIHBlcmZvcm1hbmNlIG1ldHJpYyB1c2VkIHRvIHNlbGVjdCB0aGUgb3B0aW1hbCBjb21iaW5hdGlvbiBvZiB2YWx1ZXMgb2YgaHlwZXJwYXJhbWV0ZXJzIGlzIFJNU0UuDQoNCmBgYHtyfQ0KIyBEZWZpbmUgZ3JpZCBvZiBoeXBlcnBhcmFtZXRlcnMNCmh5cGVyLmdyaWQucmVnIDwtIGV4cGFuZC5ncmlkKA0KICBsYXllcjEgPSBjKDUsIDEwLCAxNSksDQogIGxlYXJuaW5nLnJhdGUgPSBjKDAuMDEsIDAuMSksDQogIGFjdGl2YXRpb24gPSBjKCJsb2dpc3RpYyIsICJ0YW5oIikNCikNCg0KIyBJbml0aWFsaXplIHJlc3VsdHMgc3RvcmFnZQ0Kcm1zZSA9IE5VTEwNCiNsYXllcjEgPSBOVUxMDQojbGVhcm5pbmdyYXRlID0gTlVMTA0KI2FjdGl2YXRpb24gPSBOVUxMDQoNCmJlc3QucmVnLnJtc2UgPC0gSW5mDQpiZXN0LnJlZy5tb2RlbCA8LSBOVUxMDQoNCiMgUGVyZm9ybSBncmlkIHNlYXJjaA0KZm9yKGkgaW4gMTpucm93KGh5cGVyLmdyaWQucmVnKSkgew0KICAjIEdldCBjdXJyZW50IGNvbmZpZ3VyYXRpb24NCiAgbGF5ZXIgPC0gaHlwZXIuZ3JpZC5yZWckbGF5ZXIxW2ldDQogIGxyIDwtIGh5cGVyLmdyaWQucmVnJGxlYXJuaW5nLnJhdGVbaV0NCiAgYWN0IDwtIGh5cGVyLmdyaWQucmVnJGFjdGl2YXRpb25baV0NCiAgDQogICMgVHJhaW4gbW9kZWwNCiAgc2V0LnNlZWQoMTIzKQ0KICBtb2RlbC5yZWcgPC0gbmV1cmFsbmV0KA0KICAgICAgbWVkdiB+IC4sDQogICAgICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogICAgICBoaWRkZW4gPSBsYXllciwNCiAgICAgIGFjdC5mY3QgPSBhY3QsDQogICAgICBsaW5lYXIub3V0cHV0ID0gVFJVRSwgICMgRm9yIHJlZ3Jlc3Npb24NCiAgICAgIGxlYXJuaW5ncmF0ZSA9IGxyLA0KICAgICAgYWxnb3JpdGhtID0gInJwcm9wKyIsDQogICAgICBzdGVwbWF4ID0gMWU1ICkNCiAgDQoNCiAgICAjIE1ha2UgcHJlZGljdGlvbnMNCiAgICBwcmVkcy5yZWcgPC0gcHJlZGljdChtb2RlbC5yZWcsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQogICAgDQogICAgIyBDYWxjdWxhdGUgUk1TRQ0KICAgIHJtc2UucmVnIDwtIHNxcnQobWVhbigocHJlZHMucmVnIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCiAgICANCiAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICBybXNlW2ldID0gcm1zZS5yZWcNCiAgICANCiAgICAjIFVwZGF0ZSBiZXN0IG1vZGVsDQogICAgaWYocm1zZS5yZWcgPCBiZXN0LnJlZy5ybXNlKSB7DQogICAgICBiZXN0LnJlZy5ybXNlIDwtIHJtc2UucmVnDQogICAgICBiZXN0LnJlZy5tb2RlbCA8LSBtb2RlbC5yZWcgDQogICAgICBiZXN0LnJlZy5wYXJhbXMgPC0gaHlwZXIuZ3JpZC5yZWdbaSwgXQ0KICAgIH0NCn0NCg0KcmVzdWx0cy5yZWdOTiA8LSBoeXBlci5ncmlkLnJlZw0KcmVzdWx0cy5yZWdOTiRybXNlIDwtIHJtc2UNCg0KDQojIFZpZXcgcmVzdWx0cyBzb3J0ZWQgYnkgUk1TRQ0KcGFuZGVyKHJlc3VsdHMucmVnTk5bb3JkZXIocmVzdWx0cy5yZWdOTiRybXNlKSwgXVsxLF0pDQpgYGANCg0KV2l0aCB0aGUgYWJvdmUgY29tYmluYXRpb24gb2Ygb3B0aW1hbCBoeXBlcnBhcmFtZXRlciB2YWx1ZXMsIHdlIHRyYWluIHRoZSBmaW5hbCBzaW5nbGUgaGlkZGVuIGxheWVyIHBlcmNlcHRyb24gbW9kZWwuDQoNCmBgYHtyfQ0KIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgd2l0aCBiZXN0IHBhcmFtZXRlcnMNCmZpbmFsLnJlZy5tb2RlbCA8LSBuZXVyYWxuZXQoDQogIG1lZHYgfiAuLA0KICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogIGhpZGRlbiA9IGJlc3QucmVnLnBhcmFtcyRsYXllcjEsDQogIGFjdC5mY3QgPSBiZXN0LnJlZy5wYXJhbXMkYWN0aXZhdGlvbiwNCiAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogIGxlYXJuaW5ncmF0ZSA9IGJlc3QucmVnLnBhcmFtcyRsZWFybmluZ19yYXRlLA0KICBhbGdvcml0aG0gPSAicnByb3ArIiwNCiAgc3RlcG1heCA9IDFlNQ0KKQ0KDQojIFBsb3QgdGhlIG5ldXJhbCBuZXR3b3JrDQojcGxvdChmaW5hbC5yZWcubW9kZWwpDQpgYGANCg0KDQoNCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjkwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvc2luZ2xlLWhpZGRlbi1NTFAtSG91c2luZy5wbmciKQ0KYGBgDQoNCg0KVGhlIGFib3ZlIE5OIHBsb3Qgc2hvd3MgdGhlIGFyY2hpdGVjdHVyZSBvZiB0aGUgZmluYWwgb25lLWhpZGRlbi1sYXllciBwZXJjZXB0cm9uIG1vZGVsLiBOZXh0LCB3ZSB3aWxsIHVzZSBpdCB0byBtYWtlIHByZWRpY3Rpb25zLg0KDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0ZXN0IHNldA0KcHJlZC5OTjEgPC0gcHJlZGljdChmaW5hbC5yZWcubW9kZWwsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCnJtc2UuTk4xIDwtIHNxcnQobWVhbigocHJlZC5OTjEgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCm1hZS5OTjEgPC0gbWVhbihhYnMocHJlZC5OTjEgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCnIuc3F1YXJlZC5OTjEgPC0gY29yKHByZWQuTk4xICwgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCiMgY2F0KCJQZXJmb3JtYW5jZSBNZXRyaWNzOlxuIikNCiMgY2F0KCJSTVNFOiIsIHJtc2UsICJcbiIpDQojIGNhdCgiTUFFOiIsIG1hZSwgIlxuIikNCiMgY2F0KCJSLXNxdWFyZWQ6Iiwgcl9zcXVhcmVkLCAiXG4iKQ0KDQojIFBsb3QgcHJlZGljdGlvbnMgdnMgYWN0dWFsDQpwbG90Lk5OMS5kYXRhIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgUHJlZGljdGVkID0gcHJlZC5OTjEgDQopDQoNCmdncGxvdChwbG90Lk5OMS5kYXRhLCBhZXMoeCA9IEFjdHVhbCwgeSA9IFByZWRpY3RlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJkYXJrcmVkIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MC44NSwgeT0uMiwgDQogICAgICAgICAgIGxhYmVsPXBhc3RlKCJSLnNxID0iLCByb3VuZChyLnNxdWFyZWQuTk4xLDQpKSwgY29sb3I9ImJsdWUiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeD0wLjg1LCB5PS4xMywgDQogICAgICAgICAgIGxhYmVsPXBhc3RlKCJSTVNFID0iLCByb3VuZChybXNlLk5OMSw0KSksIGNvbG9yPSJibHVlIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MC44NSwgeT0uMDYsIA0KICAgICAgICAgICBsYWJlbD1wYXN0ZSgiICBNQUUgPSIsIHJvdW5kKG1hZS5OTjEsNCkpLCBjb2xvcj0iYmx1ZSIpICsNCiAgZ2d0aXRsZSgiQWN0dWFsIHZzIFByZWRpY3RlZCBWYWx1ZXMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClRoZSBmaWd1cmUgYWJvdmUgZGVtb25zdHJhdGVzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHByZWRpY3RlZCB0YXJnZXQgdmFsdWVzIGFuZCB0aGUgc2NhbGVkIHRhcmdldCB2YWx1ZXMuIEJvdGggdGhlIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBhbmQgbWVhbiBhYnNvbHV0ZSBlcnJvciAoTUFFKSBtZXRyaWNzIGFyZSBkaXNwbGF5ZWQgb24gdGhlIHBsb3QuIE5leHQsIHdlIGZpdCBhIGNsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB0byB0aGUgc2NhbGVkIGRhdGEgYW5kIHBlcmZvcm0gYSBjb21wYXJhdGl2ZSBhbmFseXNpcyBiZXR3ZWVuIHRoaXMgYmFzZWxpbmUgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIG91ciBuZXVyYWwgbmV0d29yayBtb2RlbC4NCg0KDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KGdncGxvdDIpDQojIFRyYWluIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQpsbS5tb2RlbCA8LSBsbShtZWR2IH4gLiwgZGF0YSA9IHRlc3QucmVnLmRhdGEpDQoNCiMgTWFrZSBwcmVkaWN0aW9ucw0KbG0ucHJlZGljdGlvbnMgPC0gcHJlZGljdChsbS5tb2RlbCwgdGVzdC5yZWcuZGF0YVssIC1uY29sKHRlc3QucmVnLmRhdGEpXSkNCg0KIyBDYWxjdWxhdGUgcGVyZm9ybWFuY2UgbWV0cmljcw0KbG0ucm1zZSA8LSBzcXJ0KG1lYW4oKGxtLnByZWRpY3Rpb25zIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCmxtLm1hZSA8LSBtZWFuKGFicyhsbS5wcmVkaWN0aW9ucyAtIHRlc3QucmVnLmRhdGEkbWVkdikpDQpsbS5yLnNxdWFyZWQgPC0gY29yKGxtLnByZWRpY3Rpb25zLCB0ZXN0LnJlZy5kYXRhJG1lZHYpXjINCg0KDQojIyBpbXByb3ZlbWVudHMNClJNU0UuaW1wIDwtIHJvdW5kKChsbS5ybXNlIC0gcm1zZS5OTjEpL2xtLnJtc2UgKiAxMDAsMikNCk1BRS5pbXAgPC0gcm91bmQoKGxtLm1hZSAtIG1hZS5OTjEpL2xtLm1hZSAqIDEwMCwgMikNClJzcS5pbXAgPC0gcm91bmQoKHIuc3F1YXJlZC5OTjEgLSBsbS5yLnNxdWFyZWQpL2xtLnIuc3F1YXJlZCAqIDEwMCwyKQ0KDQojIw0KUGVyZm9ybWFuY2UudGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgTE0gPSBjKGxtLnJtc2UsIGxtLm1hZSwgbG0uci5zcXVhcmVkKSwNCiAgTk4uMSA9IGMocm1zZS5OTjEsIG1hZS5OTjEsIHIuc3F1YXJlZC5OTjEpLA0KICBJbXByb3ZlbWVudC5wZXJjZW50YWdlID0gYyhSTVNFLmltcCwgTUFFLmltcCwgUnNxLmltcCkNCikNCnJvd25hbWVzKFBlcmZvcm1hbmNlLnRhYmxlKSA8LSBjKCJSTVNFIiwgIk1BRSIsICJSLnNxdWFyZSIpDQpwYW5kZXIoUGVyZm9ybWFuY2UudGFibGUpDQpgYGANCg0KDQpgYGB7cn0NCiMgUGxvdCBib3RoIHByZWRpY3Rpb25zDQpjb21wYXJpc29uLmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgQWN0dWFsID0gdGVzdC5yZWcuZGF0YSRtZWR2LA0KICBNTFAgPSBwcmVkLk5OMSwNCiAgTGluZWFyID0gbG0ucHJlZGljdGlvbnMNCikNCg0KZ2dwbG90KGNvbXBhcmlzb24uZGF0YSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTUxQLCBjb2xvciA9ICJNTFAiKSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTGluZWFyLCBjb2xvciA9ICJMaW5lYXIgUmVncmVzc2lvbiIpKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJNTFAiID0gImJsdWUiLCAiTGluZWFyIFJlZ3Jlc3Npb24iID0gInJlZCIpKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWwgQ29tcGFyaXNvbjogQWN0dWFsIHZzIFByZWRpY3RlZCIsDQogICAgICAgeCA9ICJBY3R1YWwgVmFsdWVzIiwNCiAgICAgICB5ID0gIlByZWRpY3RlZCBWYWx1ZXMiLA0KICAgICAgIGNvbG9yID0gIk1vZGVsIFR5cGUiKSArDQogIHRoZW1lKA0KICAgIHBsb3QubWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDQwLCAyMCwgMjAsIDIwLCB1bml0ID0gInB0IiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMS4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxMCkNCiAgICApDQpgYGANCg0KVGhlIGFib3ZlIHNjYXR0ZXIgcGxvdCBvZiB0aGUgdHJ1ZSB0YXJnZXQgdmFsdWVzIGFuZCBwcmVkaWN0ZWQgdmFsdWVzIGJhc2VkIG9uIHRoZSB0d28gbW9kZWxzIGFsc28gc2hvd3MgdGhhdCB0aGUgb25lLWhpZGRlbi1wZXJjZXB0cm9uIG1vZGVsIG91dHBlcmZvcm1zIHRoZSBjbGFzc2ljIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLg0KDQoNCiMjIFR3by1oaWRkZW4tbGF5ZXIgUGVyY2VwdHJvbg0KDQpUaGlzIHN1YnNlY3Rpb24gZXhwbG9yZXMgd2hldGhlciBhIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdGlvbiB0aGF0IGhhcyBtb3JlIGNvbXBsZXggYXJjaGl0ZWN0dXJlIGNvdWxkIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlLg0KDQpUaGUgbW9kZWwtYnVpbGRpbmcgcHJvY2VzcyBpcyBpZGVudGljYWwgdG8gdGhlIHByZXZpb3VzIG9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4gV2Ugd2lsbCBub3QgZGV0YWlsIHRoZSBzdGVwcyBhcyB3ZSBkaWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCmBgYHtyfQ0KIyBEZWZpbmUgZ3JpZCBvZiBoeXBlcnBhcmFtZXRlcnMNCmh5cGVyLmdyaWQuTk4yIDwtIGV4cGFuZC5ncmlkKA0KICBsYXllcjEgPSBjKDUsIDEwLCAxNSksDQogIGxheWVyMiA9IGMoMCwgMywgNSksICAjIDAgbWVhbnMgbm8gc2Vjb25kIGxheWVyDQogIGxlYXJuaW5nLnJhdGUgPSBjKDAuMDEsIDAuMSksDQogIGFjdGl2YXRpb24gPSBjKCJsb2dpc3RpYyIsICJ0YW5oIikNCikNCg0KIyBJbml0aWFsaXplIHJlc3VsdHMgaW4gc3RvcmFnZQ0KcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkNCmJlc3Qucm1zZSA8LSBJbmYNCmJlc3QubW9kZWwgPC0gTlVMTA0KDQojIFBlcmZvcm0gZ3JpZCBzZWFyY2gNCmZvcihpIGluIDE6bnJvdyhoeXBlci5ncmlkLk5OMikpIHsNCiAgIyBHZXQgY3VycmVudCBjb25maWd1cmF0aW9uDQogIGxheWVyMSA8LSBoeXBlci5ncmlkLk5OMiRsYXllcjFbaV0NCiAgbGF5ZXIyIDwtIGh5cGVyLmdyaWQuTk4yJGxheWVyMltpXQ0KICBsciA8LSBoeXBlci5ncmlkLk5OMiRsZWFybmluZy5yYXRlW2ldDQogIGFjdCA8LSBoeXBlci5ncmlkLk5OMiRhY3RpdmF0aW9uW2ldDQogIA0KICAjIENyZWF0ZSBoaWRkZW4gbGF5ZXJzIHZlY3Rvcg0KICBpZihsYXllcjIgPT0gMCkgew0KICAgIGhpZGRlbi5sYXllcnMgPC0gYyhsYXllcjEpDQogIH0gZWxzZSB7DQogICAgaGlkZGVuLmxheWVycyA8LSBjKGxheWVyMSwgbGF5ZXIyKQ0KICB9DQogIA0KICAjIFRyYWluIG1vZGVsDQogIHNldC5zZWVkKDEyMykNCiAgbW9kZWwuTk4yIDwtIHRyeUNhdGNoKHsNCiAgICBuZXVyYWxuZXQoDQogICAgICBtZWR2IH4gLiwNCiAgICAgIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgICAgIGhpZGRlbiA9IGhpZGRlbi5sYXllcnMsDQogICAgICBhY3QuZmN0ID0gYWN0LA0KICAgICAgbGluZWFyLm91dHB1dCA9IFRSVUUsICAjIEZvciByZWdyZXNzaW9uDQogICAgICBsZWFybmluZ3JhdGUgPSBsciwNCiAgICAgIGFsZ29yaXRobSA9ICJycHJvcCsiLA0KICAgICAgc3RlcG1heCA9IDFlNQ0KICAgICkNCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQ0KICANCiAgaWYoIWlzLm51bGwobW9kZWwuTk4yKSkgew0KICAgICMgTWFrZSBwcmVkaWN0aW9ucw0KICAgIHByZWRzIDwtIHByZWRpY3QobW9kZWwuTk4yLCB0ZXN0LnJlZy5kYXRhWywgLW5jb2wodGVzdC5yZWcuZGF0YSldKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIFJNU0UNCiAgICBybXNlIDwtIHNxcnQobWVhbigocHJlZHMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KICAgIA0KICAgICMgU3RvcmUgcmVzdWx0cw0KICAgIHJlc3VsdHMgPC0gcmJpbmQocmVzdWx0cywgZGF0YS5mcmFtZSgNCiAgICAgIGxheWVyMSA9IGxheWVyMSwNCiAgICAgIGxheWVyMiA9IGxheWVyMiwNCiAgICAgIGxlYXJuaW5nX3JhdGUgPSBsciwNCiAgICAgIGFjdGl2YXRpb24gPSBhY3QsDQogICAgICBybXNlID0gcm1zZQ0KICAgICkpDQogICAgDQogICAgIyBVcGRhdGUgYmVzdCBtb2RlbA0KICAgIGlmKHJtc2UgPCBiZXN0LnJtc2UpIHsNCiAgICAgIGJlc3Qucm1zZSA8LSBybXNlDQogICAgICBiZXN0Lm1vZGVsIDwtIG1vZGVsLk5OMg0KICAgICAgYmVzdC5wYXJhbXMgPC0gaHlwZXIuZ3JpZC5OTjJbaSwgXQ0KICAgIH0NCiAgfQ0KfQ0KDQojIFZpZXcgcmVzdWx0cyBzb3J0ZWQgYnkgUk1TRQ0KcmVzdWx0c1tvcmRlcihyZXN1bHRzJHJtc2UpLCBdDQpgYGANCg0KYGBge3J9DQojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB3aXRoIGJlc3QgcGFyYW1ldGVycw0KZmluYWwubW9kZWwuTk4yIDwtIG5ldXJhbG5ldCgNCiAgbWVkdiB+IC4sDQogIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgaGlkZGVuID0gaWYoYmVzdC5wYXJhbXMkbGF5ZXIyID09IDApIHsNCiAgICAgICAgYyhiZXN0LnBhcmFtcyRsYXllcjEpfSBlbHNlIHsNCiAgICAgICAgYyhiZXN0LnBhcmFtcyRsYXllcjEsIGJlc3QucGFyYW1zJGxheWVyMil9LA0KICBhY3QuZmN0ID0gYmVzdC5wYXJhbXMkYWN0aXZhdGlvbiwNCiAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogIGxlYXJuaW5ncmF0ZSA9IGJlc3QucGFyYW1zJGxlYXJuaW5nLnJhdGUsDQogIGFsZ29yaXRobSA9ICJycHJvcCsiLA0KICBzdGVwbWF4ID0gMWU1DQopDQoNCiMgUGxvdCB0aGUgbmV1cmFsIG5ldHdvcmsNCiNwbG90KGZpbmFsLm1vZGVsLk5OMikNCmBgYA0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3R3by1oaWRkZW4tTUxQLUhvdXNpbmcucG5nIikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0ZXN0IHNldA0KcHJlZC5OTjIgPC0gcHJlZGljdChmaW5hbC5tb2RlbC5OTjIsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCnJtc2UuTk4yIDwtIHNxcnQobWVhbigocHJlZC5OTjIgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCm1hZS5OTjIgPC0gbWVhbihhYnMocHJlZC5OTjIgIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCnIuc3F1YXJlZC5OTjIgPC0gY29yKHByZWQuTk4yICwgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCiMjIHZlY3RvciBvZiBlcnJvciBtZXRyaWMNCk5OMiA8LSBjKHJtc2UuTk4yLCBtYWUuTk4yLCByLnNxdWFyZWQuTk4yKQ0KDQojIFBsb3QgcHJlZGljdGlvbnMgdnMgYWN0dWFsDQpwbG90LmRhdGEuTk4yIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgUHJlZGljdGVkID0gcHJlZC5OTjIgDQopDQoNCmdncGxvdChwbG90LmRhdGEuTk4yLCBhZXMoeCA9IEFjdHVhbCwgeSA9IFByZWRpY3RlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiKSArDQogIGdndGl0bGUoIkFjdHVhbCB2cyBQcmVkaWN0ZWQgVmFsdWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KIyBUcmFpbiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbA0KbG0ubW9kZWwgPC0gbG0obWVkdiB+IC4sIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSkNCg0KIyBNYWtlIHByZWRpY3Rpb25zDQpsbS5wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGxtLm1vZGVsLCB0ZXN0LnJlZy5kYXRhWywgLW5jb2wodGVzdC5yZWcuZGF0YSldKQ0KDQojIENhbGN1bGF0ZSBwZXJmb3JtYW5jZSBtZXRyaWNzDQpsbS5ybXNlIDwtIHNxcnQobWVhbigobG0ucHJlZGljdGlvbnMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KbG0ubWFlIDwtIG1lYW4oYWJzKGxtLnByZWRpY3Rpb25zIC0gdGVzdC5yZWcuZGF0YSRtZWR2KSkNCmxtLnIuc3F1YXJlZCA8LSBjb3IobG0ucHJlZGljdGlvbnMsIHRlc3QucmVnLmRhdGEkbWVkdileMg0KDQojIyMNCnJtc2UuTk4yLmltcCA8LSAobG0ucm1zZSAtIHJtc2UuTk4yKS9sbS5ybXNlKjEwMA0KbWFlLk5OMi5pbXAgPC0gKGxtLm1hZSAtIG1hZS5OTjIpL2xtLm1hZSAqIDEwMA0KUnNxLk5OMi5pbXAgPC0gKHIuc3F1YXJlZC5OTjIgLSBsbS5yLnNxdWFyZWQpL2xtLnIuc3F1YXJlZCAqIDEwMA0KDQpOTjIuaW1wcm92ZSA8LWMocm1zZS5OTjIuaW1wLCBtYWUuTk4yLmltcCwgUnNxLk5OMi5pbXApDQoNCnBlcmYubWF0cml4IDwtZGF0YS5mcmFtZSgNCiAgICAgICAgICAgICAgTE0gPSBjKGxtLnJtc2UsIGxtLm1hZSwgbG0uci5zcXVhcmVkKSwNCiAgICAgICAgICAgICAgTk4uMSA9IGMocm1zZS5OTjEsIG1hZS5OTjEsIHIuc3F1YXJlZC5OTjEpLA0KICAgICAgICAgICAgICBOTi4yID0gYyhybXNlLk5OMiwgbWFlLk5OMiwgci5zcXVhcmVkLk5OMikNCiAgICAgICAgICkNCg0KcGVyZi5tYXRyaXgkTk4xLkltcHJvdmUgPC0gcm91bmQoMTAwKihwZXJmLm1hdHJpeCRMTS1wZXJmLm1hdHJpeCROTi4xKS9wZXJmLm1hdHJpeCRMTSwyKQ0KcGVyZi5tYXRyaXgkTk4yLkltcHJvdmUgPC0gcm91bmQoMTAwKihwZXJmLm1hdHJpeCRMTS1wZXJmLm1hdHJpeCROTi4yKS9wZXJmLm1hdHJpeCRMTSwyKQ0KDQpyb3duYW1lcyhwZXJmLm1hdHJpeCkgPC0gYygiUk1TRSIsICJNQUUiLCAiUi5zcSIpDQpwYW5kZXIocGVyZi5tYXRyaXgpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIFBsb3QgYm90aCBwcmVkaWN0aW9ucw0KY29tcGFyaXNvbi5kYXRhLk5OMiA8LSBkYXRhLmZyYW1lKA0KICBBY3R1YWwgPSB0ZXN0LnJlZy5kYXRhJG1lZHYsDQogIE1MUCA9IHByZWQuTk4yICwNCiAgTGluZWFyID0gbG0ucHJlZGljdGlvbnMNCikNCg0KZ2dwbG90KGNvbXBhcmlzb24uZGF0YS5OTjIpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IE1MUCwgY29sb3IgPSAiTUxQIikpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IExpbmVhciwgY29sb3IgPSAiTGluZWFyIFJlZ3Jlc3Npb24iKSkgKw0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTUxQIiA9ICJibHVlIiwgIkxpbmVhciBSZWdyZXNzaW9uIiA9ICJyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsIENvbXBhcmlzb246IEFjdHVhbCB2cyBQcmVkaWN0ZWQiLA0KICAgICAgIHggPSAiQWN0dWFsIFZhbHVlcyIsDQogICAgICAgeSA9ICJQcmVkaWN0ZWQgVmFsdWVzIiwNCiAgICAgICBjb2xvciA9ICJNb2RlbCBUeXBlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMgTUxQIENsYXNzaWZpY2F0aW9uDQoNClRoZSBtb2RlbGluZyBwcm9jZXNzIGZvciBNTFAgY2xhc3NpZmljYXRpb24gaXMgaWRlbnRpY2FsIHRvIHRoYXQgb2YgTUxQIHJlZ3Jlc3Npb24uIEFzIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiBvbiBNTFAgcmVncmVzc2lvbiwgd2Ugd2lsbCBkb2N1bWVudCB0aGUgbW9kZWxpbmcgcHJvY2VzcyBpbiBkZXRhaWwuDQoNClRoZSAqKlBpbWEgSW5kaWFuIERpYWJldGVzKiogZGF0YXNldCBjb250YWlucyBvbmx5IG51bWVyaWNhbCBmZWF0dXJlIHZhcmlhYmxlcy4gVGhlcmVmb3JlLCB3ZSB3aWxsIGFwcGx5IG1pbi1tYXggc2NhbGluZyB0byBhbGwgZmVhdHVyZXMgYW5kIGNvbnZlcnQgdGhlIHRhcmdldCB2YXJpYWJsZSBpbnRvIGEgZmFjdG9yIHZhcmlhYmxlLg0KDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCiMgbGlicmFyeShuZXVyYWxuZXQpDQojIGxpYnJhcnkocFJPQykgICAgICMgRm9yIFJPQyBhbmFseXNpcw0KIyBsaWJyYXJ5KGdncGxvdDIpICAjIEZvciB2aXN1YWxpemF0aW9uDQoNCiMgTG9hZCB0aGUgUGltYSBJbmRpYW5zIERpYWJldGVzIGRhdGFzZXQNCmRhdGEoIlBpbWFJbmRpYW5zRGlhYmV0ZXMyIiwgcGFja2FnZSA9ICJtbGJlbmNoIikNCg0KIyMgcmVtb3ZpbmcgcmVjb3JkcyB3aXRoIG1pc3NpbmcgY29tcG9uZW50OyBpbXB1dGF0aW9uIHNob3VsZCBiZSBjb25zaWRlcmVkIGluDQojIyBwcmFjdGljYWwgYXBwbGljYXRpb25zDQpkaWFiZXRlcy5kYXRhIDwtIG5hLm9taXQoUGltYUluZGlhbnNEaWFiZXRlczIpICANCg0KIyBGZWF0dXJlIHNjYWxpbmcgLSBub3JtYWxpemUgbnVtZXJpYyB2YXJpYWJsZXMgdG8gWzAsMV0gcmFuZ2UNCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7DQogIHJldHVybiAoKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQojIEFwcGx5IG5vcm1hbGl6YXRpb24gdG8gYWxsIG51bWVyaWMgY29sdW1ucw0KbnVtZXJpYy5jb2xzIDwtIHNhcHBseShkaWFiZXRlcy5kYXRhLCBpcy5udW1lcmljKQ0KZGlhYmV0ZXMuZGF0YVtudW1lcmljLmNvbHNdIDwtIGxhcHBseShkaWFiZXRlcy5kYXRhW251bWVyaWMuY29sc10sIG5vcm1hbGl6ZSkNCg0KIyBFbmNvZGUgdGhlIHRhcmdldCB2YXJpYWJsZSAoZGlhYmV0ZXMpIGFzIG51bWVyaWMgKDAvMSkNCmRpYWJldGVzLmRhdGEkZGlhYmV0ZXMgPC0gaWZlbHNlKGRpYWJldGVzLmRhdGEkZGlhYmV0ZXMgPT0gInBvcyIsIDEsIDApDQoNCiMgVHdvLXdheSBkYXRhIHNwbGl0dGluZzogNzAtMzAlDQpzZXQuc2VlZCgxMjMpICAjIEZvciByZXByb2R1Y2liaWxpdHkNCnNhbXBsZS5zaXplLmNscyA8LSBmbG9vcigwLjcwICogbnJvdyhkaWFiZXRlcy5kYXRhKSkNCnRyYWluLmluZGljZXMuY2xzIDwtIHNhbXBsZSgxOnNhbXBsZS5zaXplLmNscywgc2l6ZSA9IHNhbXBsZS5zaXplLmNscywgcmVwbGFjZSA9IEZBTFNFKQ0KDQp0cmFpbi5kYXRhLmNscyA8LSBkaWFiZXRlcy5kYXRhW3RyYWluLmluZGljZXMuY2xzLCBdDQp0ZXN0LmRhdGEuY2xzIDwtIGRpYWJldGVzLmRhdGFbLXRyYWluLmluZGljZXMuY2xzLCBdDQpgYGANCg0KDQpUbyBzaW1wbGlmeSBoeXBlcnBhcmFtZXRlciB0dW5pbmcgYW5kIGZpbmFsIG1vZGVsIHRyYWluaW5nIHdpdGggdGhlIHByZS1zZWxlY3RlZCBNTFAgYXJjaGl0ZWN0dXJlIGZvciBjbGFzc2lmaWNhdGlvbiwgd2UgZGVmaW5lIGEgY3VzdG9tIGZ1bmN0aW9uIHRvIGRldGVybWluZSB0aGUgb3B0aW1hbCBudW1iZXIgb2Ygbm9kZXMgZm9yIGJvdGggc2luZ2xlLWhpZGRlbi1sYXllciBhbmQgZG91YmxlLWhpZGRlbi1sYXllciBNTFBzLiBUaGUgY3JpdGVyaW9uIGZvciBzZWxlY3RpbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzIGlzIHRoZSBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgKEFVQykgYXMgdGhlIGV2YWx1YXRpb24gbWV0cmljLCB0aG91Z2ggYWNjdXJhY3kgYmFzZWQgb24gdGhlIGRlZmF1bHQgMC41IHRocmVzaG9sZHMgY291bGQgYWx0ZXJuYXRpdmVseSBiZSB1c2VkLiBXZSBjaG9vc2UgdGhlIGxvZ2lzdGljIGFjdGl2YXRpb24gZnVuY3Rpb24gd2hpbGUga2VlcGluZyBhbGwgb3RoZXIgaHlwZXJwYXJhbWV0ZXJzIGF0IHRoZWlyIGRlZmF1bHQgdmFsdWVzLg0KDQoNCmBgYHtyfQ0KIyBGdW5jdGlvbiB0byBwZXJmb3JtIGdyaWQgc2VhcmNoIGZvciBuZXVyYWxuZXQNCm5ldXJhbG5ldC5ncmlkLnNlYXJjaCA8LSBmdW5jdGlvbih0cmFpbi5kYXRhLCB0ZXN0LmRhdGEsIGhpZGRlbi5sYXllcnMgPSAxKSB7DQogICMgRGVmaW5lIHRoZSBncmlkIG9mIGh5cGVycGFyYW1ldGVycw0KICBpZiAoaGlkZGVuLmxheWVycyA9PSAxKSB7DQogICAgaGlkZGVuLm5vZGVzIDwtIGMoMiwgNCwgNiwgOCwgMTApDQogICAgZ3JpZCA8LSBleHBhbmQuZ3JpZChoaWRkZW4gPSBoaWRkZW4ubm9kZXMpDQogIH0gZWxzZSB7DQogICAgaGlkZGVuLm5vZGVzIDwtIGMoMiwgNCwgNikNCiAgICBncmlkIDwtIGV4cGFuZC5ncmlkKGhpZGRlbjEgPSBoaWRkZW4ubm9kZXMsIGhpZGRlbjIgPSBoaWRkZW4ubm9kZXMpDQogIH0NCiAgDQogICMgQWRkIGNvbHVtbnMgdG8gc3RvcmUgcmVzdWx0cw0KICBncmlkJGFjY3VyYWN5IDwtIE5BDQogIGdyaWQkYXVjIDwtIE5BDQogIA0KICAjIEZvcm11bGEgZm9yIG5ldXJhbCBuZXR3b3JrDQogIG5uLmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhYmV0ZXMgfiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyh0cmFpbi5kYXRhKVshbmFtZXModHJhaW4uZGF0YSkgJWluJSAiZGlhYmV0ZXMiXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiArICIpKSkNCiAgDQogICMgUGVyZm9ybSBncmlkIHNlYXJjaA0KICBmb3IgKGkgaW4gMTpucm93KGdyaWQpKSB7DQogICAgaWYgKGhpZGRlbi5sYXllcnMgPT0gMSkgew0KICAgICAgaGlkZGVuIDwtIGMoZ3JpZCRoaWRkZW5baV0pDQogICAgfSBlbHNlIHsNCiAgICAgIGhpZGRlbiA8LSBjKGdyaWQkaGlkZGVuMVtpXSwgZ3JpZCRoaWRkZW4yW2ldKQ0KICAgIH0NCiAgICANCiAgICAjIFRyYWluIHRoZSBtb2RlbA0KICAgIG5uLm1vZGVsIDwtIG5ldXJhbG5ldCgNCiAgICAgIGZvcm11bGEgPSBubi5mb3JtdWxhLA0KICAgICAgZGF0YSA9IHRyYWluLmRhdGEsDQogICAgICBoaWRkZW4gPSBoaWRkZW4sDQogICAgICBsaW5lYXIub3V0cHV0ID0gRkFMU0UsICAjIEZvciBjbGFzc2lmaWNhdGlvbg0KICAgICAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsICAgIyBTaWdtb2lkIGFjdGl2YXRpb24NCiAgICAgIHN0ZXBtYXggPSAxZTYgICAgICAgICAgICMgSW5jcmVhc2UgbWF4IHN0ZXBzIGZvciBjb252ZXJnZW5jZQ0KICAgICkNCiAgICANCiAgICAjIE1ha2UgcHJlZGljdGlvbnMNCiAgICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG5uLm1vZGVsLCB0ZXN0LmRhdGEpDQogICAgcHJlZGljdGVkLmNsYXNzZXMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID4gMC41LCAxLCAwKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIGFjY3VyYWN5DQogICAgYWNjdXJhY3kgPC0gbWVhbihwcmVkaWN0ZWQuY2xhc3NlcyA9PSB0ZXN0LmRhdGEkZGlhYmV0ZXMpDQogICAgDQogICAgIyBDYWxjdWxhdGUgQVVDDQogICAgcm9jLm9iaiA8LSByb2ModGVzdC5kYXRhJGRpYWJldGVzLCBwcmVkaWN0aW9ucykNCiAgICBhdWMudmFsdWUgPC0gYXVjKHJvYy5vYmopDQogICAgDQogICAgIyBTdG9yZSByZXN1bHRzDQogICAgZ3JpZCRhY2N1cmFjeVtpXSA8LSBhY2N1cmFjeQ0KICAgIGdyaWQkYXVjW2ldIDwtIGF1Yy52YWx1ZQ0KICB9DQogIHJldHVybihncmlkKQ0KfQ0KYGBgDQoNCg0KVGhlIHBlcmZvcm1hbmNlIHRhYmxlIG9mIGNvcnJlc3BvbmRpbmcgbnVtYmVyIG9mIG5vZGVzIGluIG9uZS1oaWRkZW4tbGF5ZXIgTUxQIGlzIGdpdmVuIGJlbG93Lg0KDQpgYGB7cn0NCiMgUGVyZm9ybSBncmlkIHNlYXJjaCBmb3Igc2luZ2xlIGhpZGRlbiBsYXllcg0KZ3JpZC5yZXN1bHRzLjFsYXllciA8LSBuZXVyYWxuZXQuZ3JpZC5zZWFyY2godHJhaW4uZGF0YT10cmFpbi5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LmRhdGE9dGVzdC5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4ubGF5ZXJzID0gMSkNCnBhbmRlcihncmlkLnJlc3VsdHMuMWxheWVyKQ0KYGBgDQoNClRoZSBvcHRpbWFsIG51bWJlciBvZiBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyIGlzIHRoZSBjb3JyZXNwb25kcyB0byB0aGUgc21hbGxlc3QgQVVDLiBTaW1pbGFybHksIHRoZSBwZXJmb3JtYW5jZSB0YWJsZSBvZiB0d28taGlkZGVuLWxheWVyIE1MUCBpcyBnaXZlbiBiZWxvdy4gDQoNCmBgYHtyfQ0KIyBQZXJmb3JtIGdyaWQgc2VhcmNoIGZvciB0d28gaGlkZGVuIGxheWVycw0KZ3JpZC5yZXN1bHRzLjJsYXllciA8LSBuZXVyYWxuZXQuZ3JpZC5zZWFyY2godHJhaW4uZGF0YT10cmFpbi5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LmRhdGE9dGVzdC5kYXRhLmNscywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4ubGF5ZXJzID0gMikNCnBhbmRlcihncmlkLnJlc3VsdHMuMmxheWVyKQ0KYGBgDQoNCioqT25lLWhpZGRlbi1sYXllciBNTFAqKg0KDQpXZSB1c2UgdGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzIHRvIGZpdHRoZSBvbmVoaWRkZW4tbGF5ZXIgTUxQIHRvIHRoZSBkYXRhIGFuZiBvYnRhaW4NCg0KDQpgYGB7cn0NCiMgRm9ybXVsYSBmb3IgbmV1cmFsIG5ldHdvcmsNCm5uLmZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhYmV0ZXMgfiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXModHJhaW4uZGF0YS5jbHMpWyFuYW1lcyh0cmFpbi5kYXRhLmNscykgJWluJSAiZGlhYmV0ZXMiXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgKyAiKSkpDQoNCiMgVHJhaW4gc2luZ2xlIGhpZGRlbiBsYXllciBtb2RlbCAodXNpbmcgYmVzdCBjb25maWd1cmF0aW9uIGZyb20gZ3JpZCBzZWFyY2gpDQpiZXN0LjFsYXllciA8LSBncmlkLnJlc3VsdHMuMWxheWVyW3doaWNoLm1heChncmlkLnJlc3VsdHMuMWxheWVyJGF1YyksIF0NCg0Kbm4uMWxheWVyIDwtIG5ldXJhbG5ldCgNCiAgZm9ybXVsYSA9IG5uLmZvcm11bGEsDQogIGRhdGEgPSB0cmFpbi5kYXRhLmNscywNCiAgaGlkZGVuID0gYmVzdC4xbGF5ZXIkaGlkZGVuLA0KICBsaW5lYXIub3V0cHV0ID0gRkFMU0UsDQogIGFjdC5mY3QgPSAibG9naXN0aWMiLA0KICBzdGVwbWF4ID0gMWU2DQopDQojIw0KI3Bsb3Qobm4uMWxheWVyLCBtYWluID0gcGFzdGUoIk9uZS1oaWRkZW4tbGF5ZXIgd2l0aCIsIGJlc3QuMWxheWVyJGhpZGRlbiwgIk5vZGVzIikpDQpgYGANCg0KYGBge3IgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9zaW5nbGUtaGlkZGVuLU1MUC1EaWFiZXRlcy5wbmciKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgVHJhaW4gdHdvIGhpZGRlbiBsYXllcnMgbW9kZWwgKHVzaW5nIGJlc3QgY29uZmlndXJhdGlvbiBmcm9tIGdyaWQgc2VhcmNoKQ0KYmVzdC4ybGF5ZXIgPC0gZ3JpZC5yZXN1bHRzLjJsYXllclt3aGljaC5tYXgoZ3JpZC5yZXN1bHRzLjJsYXllciRhdWMpLCBdDQoNCm5uLjJsYXllciA8LSBuZXVyYWxuZXQoDQogIGZvcm11bGEgPSBubi5mb3JtdWxhLA0KICBkYXRhID0gdHJhaW4uZGF0YS5jbHMsDQogIGhpZGRlbiA9IGMoYmVzdC4ybGF5ZXIkaGlkZGVuMSwgYmVzdC4ybGF5ZXIkaGlkZGVuMiksDQogIGxpbmVhci5vdXRwdXQgPSBGQUxTRSwNCiAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsDQogIHN0ZXBtYXggPSAxZTYNCikNCiMjDQojcGxvdChubi4ybGF5ZXIpDQpgYGANCg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3R3by1oaWRkZW4tTUxQLURpYWJldGVzLnBuZyIpDQpgYGANCg0KDQpJbiB0aGUgdHdvIG1vZGVsIHBsb3RzIGFib3ZlLCB0aGUgKipFcnJvcioqIGFuZCAqKlN0ZXBzKiogdmFsdWVzIGRpc3BsYXllZCBhdCB0aGUgYm90dG9tIHJlcHJlc2VudDoNCg0KKiAqKlN0ZXBzKio6IFRoZSBudW1iZXIgb2YgKip0cmFpbmluZyBpdGVyYXRpb25zKiogKGVwb2NocykgY29tcGxldGVkIGR1cmluZyBtb2RlbCBvcHRpbWl6YXRpb24uIEVhY2ggc3RlcCBjb3JyZXNwb25kcyB0byBvbmUgY29tcGxldGUgZm9yd2FyZC9iYWNrd2FyZCBwYXNzIGFuZCB3ZWlnaHQgdXBkYXRlIGN5Y2xlLg0KDQoqICoqRXJyb3JzKio6IFRoZSAqKnRyYWluaW5nIGVycm9yKiogcmVmbGVjdHMgdGhlIGxvc3MgZnVuY3Rpb24gdmFsdWUgKHR5cGljYWxseSBTU0UgZm9yIHJlZ3Jlc3Npb24gb3IgY3Jvc3MtZW50cm9weSBmb3IgY2xhc3NpZmljYXRpb24pLiBUaGUgZGlzcGxheWVkICoqRXJyb3IqKiByZXByZXNlbnRzIHRoZSBmaW5hbCBlcnJvciB2YWx1ZSBhY2hpZXZlZCB3aGVuIHRoZSBvcHRpbWl6YXRpb24gcHJvY2VzcyBjb252ZXJnZXMuDQoNCg0KTmV4dCwgd2Ugd3JpdGUgYSBjdXN0b20gZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyB0byBhc3Nlc3MgdGhlIGdsb2JhbCBwZXJmb3JtYW5jZSB0aHJvdWdoIFJPQyBjdXJ2ZXMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGFyZWFzIHVuZGVyIHRoZSBST0MgY3VydmVzLg0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gdG8gZXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2UNCmV2YWx1YXRlLm1vZGVsIDwtIGZ1bmN0aW9uKG1vZGVsLCB0ZXN0LmRhdGEpIHsNCiAgIyBNYWtlIHByZWRpY3Rpb25zDQogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIHRlc3QuZGF0YSkNCiAgcHJlZGljdGVkLmNsYXNzZXMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID4gMC41LCAxLCAwKQ0KICANCiAgIyBDYWxjdWxhdGUgbWV0cmljcw0KICBhY2N1cmFjeSA8LSBtZWFuKHByZWRpY3RlZC5jbGFzc2VzID09IHRlc3QuZGF0YSRkaWFiZXRlcykNCiAgY29uZnVzaW9uLm1hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0ZWQuY2xhc3NlcywgQWN0dWFsID0gdGVzdC5kYXRhJGRpYWJldGVzKQ0KICByb2Mub2JqIDwtIHJvYyh0ZXN0LmRhdGEkZGlhYmV0ZXMsIHByZWRpY3Rpb25zKQ0KICBhdWMudmFsdWUgPC0gYXVjKHJvYy5vYmopDQogIA0KICByZXR1cm4obGlzdCgNCiAgICBhY2N1cmFjeSA9IGFjY3VyYWN5LA0KICAgIGNvbmZ1c2lvbi5tYXRyaXggPSBjb25mdXNpb24ubWF0cml4LA0KICAgIHJvYy5vYmogPSByb2Mub2JqLA0KICAgIGF1YyA9IGF1Yy52YWx1ZQ0KICApKQ0KfQ0KDQojIEV2YWx1YXRlIHNpbmdsZSBoaWRkZW4gbGF5ZXIgbW9kZWwNCnBlcmYuMWxheWVyIDwtIGV2YWx1YXRlLm1vZGVsKG5uLjFsYXllciwgdGVzdC5kYXRhLmNscykNCiNwcmludChwZXJmLjFsYXllcltjKCJhY2N1cmFjeSIsICJjb25mdXNpb25fbWF0cml4IiwgImF1YyIpXSkNCg0KIyBFdmFsdWF0ZSB0d28gaGlkZGVuIGxheWVycyBtb2RlbA0KcGVyZi4ybGF5ZXIgPC0gZXZhbHVhdGUubW9kZWwobm4uMmxheWVyLCB0ZXN0LmRhdGEuY2xzKQ0KI3ByaW50KHBlcmYuMmxheWVyW2MoImFjY3VyYWN5IiwgImNvbmZ1c2lvbl9tYXRyaXgiLCAiYXVjIildKQ0KYGBgDQoNCg0KV2UgdXNlIGNsYXNzaWMgbG9naXN0aWMgcmVncmVzc2lvbiBhcyB0aGUgYmFzZSBtb2RlbCBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSB0d28gTUxQcyB1c2luZyBST0MgY3VydmVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIEFVQyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuDQoNCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9DQojIFRyYWluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgKGJhc2UgbW9kZWwpDQpsb2dpdC5tb2RlbCA8LSBnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW4uZGF0YS5jbHMsIGZhbWlseSA9IGJpbm9taWFsKQ0KDQojIEV2YWx1YXRlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmxvZ2l0LnByZWQgPC0gcHJlZGljdChsb2dpdC5tb2RlbCwgdGVzdC5kYXRhLmNscywgdHlwZSA9ICJyZXNwb25zZSIpDQpsb2dpdC5jbGFzc2VzIDwtIGlmZWxzZShsb2dpdC5wcmVkID4gMC41LCAxLCAwKQ0KbG9naXQuYWNjdXJhY3kgPC0gbWVhbihsb2dpdC5jbGFzc2VzID09IHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMpDQpsb2dpdC5yb2MgPC0gcm9jKHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMsIGxvZ2l0LnByZWQpDQpsb2dpdC5hdWMgPC0gYXVjKGxvZ2l0LnJvYykNCg0KIyMNCnJvYy4xbGF5ZXIgPC0gcGVyZi4xbGF5ZXIkcm9jLm9iag0Kcm9jLjJsYXllciA8LSBwZXJmLjJsYXllciRyb2Mub2JqDQpyb2MubG9naXQgPC0gbG9naXQucm9jDQoNCiMjIHNwZWNpZmljaXR5IGFuZCBzZW5zaXRpdml0eQ0Kc2VuLjFsYXllciA8LSByb2MuMWxheWVyJHNlbnNpdGl2aXRpZXMNCnNwZS4xbGF5ZXIgPC0gcm9jLjFsYXllciRzcGVjaWZpY2l0aWVzDQpzZW4uMmxheWVyIDwtIHJvYy4ybGF5ZXIkc2Vuc2l0aXZpdGllcw0Kc3BlLjJsYXllciA8LSByb2MuMmxheWVyJHNwZWNpZmljaXRpZXMNCnNlbi5sb2dpdCA8LSByb2MubG9naXQkc2Vuc2l0aXZpdGllcw0Kc3BlLmxvZ2l0IDwtIHJvYy5sb2dpdCRzcGVjaWZpY2l0aWVzDQoNCiMjIEFVQw0KYXVjLjFsYXllciA8LSByb2MuMWxheWVyJGF1Yw0KYXVjLjJsYXllciA8LSByb2MuMmxheWVyJGF1Yw0KYXVjLmxvZ2l0IDwtIHJvYy5sb2dpdCRhdWMNCg0KIyMgUGxvdCBST0MgY3VydmVzIGZvciBjb21wYXJpc29uDQpwYXIocHR5ID0gInMiKSAgICMgbWFrZSBhIHNxdWFyZSBwbG90IHRvIGF2YW9pZCBkaXN0b3J0aW9uDQpwbG90KDEtc3BlLjFsYXllciwgc2VuLjFsYXllciwgdHlwZSA9ICJsIiwgbHR5ID0gMSwNCiAgICAgY29sID0gImJsdWUiLCANCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLA0KICAgICB5bGFiID0gInNlbnNpdHZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZSBDb21wYXJpc29uIikNCg0KbGluZXMoMS1zcGUuMmxheWVyLCBzZW4uMmxheWVyLCBsdHkgPSAxLCBjb2wgPSAiZGFya3JlZCIpDQpsaW5lcygxLXNwZS5sb2dpdCwgc2VuLmxvZ2l0LCBsdHkgPSAxLCBjb2wgPSAiZGFya2dyZWVuIikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCANCiAgICAgICBsZWdlbmQgPSBjKHBhc3RlKCIxLWxheWVyIE1MUCAoQVVDID0iLCByb3VuZChwZXJmLjFsYXllciRhdWMsIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgcGFzdGUoIjItbGF5ZXIgTUxQIChBVUMgPSIsIHJvdW5kKHBlcmYuMmxheWVyJGF1YywgMyksICIpIiksDQogICAgICAgICAgICAgICAgICBwYXN0ZSgiTG9naXN0aWMgUmVnIChBVUMgPSIsIHJvdW5kKGxvZ2l0LmF1YywgMyksICIpIikpLA0KICAgICAgICAgICAgICAgIGNvbCA9IGMoImJsdWUiLCAiZGFya3JlZCIsICJkYXJrZ3JlZW4iKSwgDQogICAgICAgICAgICAgICAgbHR5ID0gMSwgY2V4ID0gMC43LCBidHkgPSAibiIpDQpgYGANCg0KVGhlIFJPQyBhbmFseXNpcyBzaG93cyB0aGF0IHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgcGVyZm9ybXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gdGhlIHR3byBNTFBzLiBUaGUgdHdvIE1MUHMgcGVyZm9ybSBlcXVhbGx5IHdlbGwsIGV2ZW4gdGhvdWdoIHRoZSB0d28taGlkZGVuLWxheWVyIE1MUCBoYXMgbW9yZSBwYXJhbWV0ZXJzICh3ZWlnaHRzKSBhbmQgaXMgbW9yZSBjb21wbGV4IHRoYW4gdGhlIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQLiBJbiBnZW5lcmFsLCBhIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQIGlzIHJlY29tbWVuZGVkIGlmIGFuIE1MUCBpcyB1c2VkIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KDQoNCiMgTUxQIG9yIENsYXNzaWNhbCBNb2RlbHM/DQoNCg0KV2UgaGF2ZSBkaXNjdXNzZWQgdGhlIE1MUCBhcmNoaXRlY3R1cmUgYW5kIHRoZSBwcm9jZXNzIG9mIGltcGxlbWVudGluZyBNTFBzIGFsb25nc2lkZSBjbGFzc2ljIG1vZGVscy4gQ2FzZSBzdHVkaWVzIHVzaW5nIHRoZSAqKkJvc3RvbiBIb3VzaW5nIERhdGEqKiBkZW1vbnN0cmF0ZSB0aGF0IE1MUCByZWdyZXNzaW9uIG91dHBlcmZvcm1zIGNsYXNzaWMgbGluZWFyIHJlZ3Jlc3Npb24sIHdoaWxlIHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcGVyZm9ybXMgYmV0dGVyIHRoYW4gTUxQIGNsYXNzaWZpY2F0aW9uIGluIHRoZSAqKlBpbWEgSW5kaWFuIERpYWJldGVzKiogZGF0YXNldCBhbmFseXNpcy4gTmV4dCwgd2Ugd2lsbCBwcmVzZW50IGEgZ2VuZXJhbCBjb21wYXJpc29uIGJldHdlZW4gdGhlc2UgbW9kZWxzLg0KDQpcDQoNCioqTUxQIENsYXNzaWZpY2F0aW9uIHZzLiBMb2dpc3RpYyBSZWdyZXNzaW9uKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiBtdWx0aWxheWVyIHBlcmNlcHRyb24gKE1MUCkgY2xhc3NpZmllcnMgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaGlnaGxpZ2h0cyBhIGZ1bmRhbWVudGFsIHRyYWRlLW9mZiBiZXR3ZWVuIGZsZXhpYmlsaXR5IGFuZCBzaW1wbGljaXR5LiANCg0KKiBMb2dpc3RpYyByZWdyZXNzaW9uLCBhcyBhIGxpbmVhciBjbGFzc2lmaWVyLCBwZXJmb3JtcyB3ZWxsIHdoZW4gZGVjaXNpb24gYm91bmRhcmllcyBhcmUgYXBwcm94aW1hdGVseSBsaW5lYXIsIG9mZmVyaW5nIGhpZ2ggaW50ZXJwcmV0YWJpbGl0eSwgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LCBhbmQgcm9idXN0bmVzcyB0byBvdmVyZml0dGluZ+KAlHBhcnRpY3VsYXJseSB3aXRoIGxpbWl0ZWQgZGF0YS4gDQoNCiogTUxQcywgd2l0aCB0aGVpciBub25saW5lYXIgYWN0aXZhdGlvbiBmdW5jdGlvbnMsIGNhbiBtb2RlbCBtb3JlIGNvbXBsZXggZGVjaXNpb24gYm91bmRhcmllcyBidXQgcmVxdWlyZSBjYXJlZnVsIHR1bmluZyBvZiBhcmNoaXRlY3R1cmUgYW5kIHJlZ3VsYXJpemF0aW9uIHRvIGF2b2lkIG92ZXJmaXR0aW5nLiANCg0KKiBUaGUgYWRkZWQgY29tcGxleGl0eSBvZiBuZXVyYWwgbmV0d29ya3MgbWF5IG5vdCBhbHdheXMgYmUganVzdGlmaWVkIGZvciBzaW1wbGVyIGNsYXNzaWZpY2F0aW9uIHRhc2tzLiBIb3dldmVyLCBNTFBzIHJlbWFpbiB2YWx1YWJsZSB3aGVuIGRlYWxpbmcgd2l0aCBoaWdobHkgbm9ubGluZWFyIGRhdGEgd2hlcmUgbG9naXN0aWMgcmVncmVzc2lvbuKAmXMgbGluZWFyIGFzc3VtcHRpb25zIGZhaWwuDQoNClwNCg0KKipNTFAgUmVncmVzc2lvbiB2cy4gQ2xhc3NpYyBMaW5lYXIgUmVncmVzc2lvbioqDQoNClNpbWlsYXIgdG8gdGhlaXIgY2xhc3NpZmljYXRpb24gY291bnRlcnBhcnRzLCBNTFAgcmVncmVzc2lvbiBtb2RlbHMgYW5kIGNsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBzZXJ2ZSBkaWZmZXJlbnQgcHVycG9zZXMgZGVwZW5kaW5nIG9uIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuIA0KDQoqIExpbmVhciByZWdyZXNzaW9uIGlzIG9wdGltYWwgd2hlbiByZWxhdGlvbnNoaXBzIGJldHdlZW4gcHJlZGljdG9ycyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSBhcmUgbGluZWFyLCBwcm92aWRpbmcgaW50ZXJwcmV0YWJsZSBjb2VmZmljaWVudHMsIGZhc3QgdHJhaW5pbmcsIGFuZCBsb3cgcmlzayBvZiBvdmVyZml0dGluZy4gDQoNCiogTUxQIHJlZ3Jlc3Npb24sIGhvd2V2ZXIsIGV4Y2VscyBpbiBjYXB0dXJpbmcgbm9ubGluZWFyIGFuZCBpbnRlcmFjdGl2ZSBlZmZlY3RzLCBtYWtpbmcgaXQgc3VpdGFibGUgZm9yIG1vcmUgY29tcGxleCByZWdyZXNzaW9uIHRhc2tzIHdoZXJlIGxpbmVhciBtb2RlbHMgdW5kZXJwZXJmb3JtLiANCg0KKiBUaGUgdHJhZGUtb2ZmIGxpZXMgaW4gY29tcHV0YXRpb25hbCBjb3N0LCB0dW5pbmcgY29tcGxleGl0eSwgYW5kIHRoZSBuZWVkIGZvciBsYXJnZXIgZGF0YXNldHMgdG8gcHJldmVudCAqKm92ZXJmaXR0aW5nKiouIElmIHRoZSB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlIGlzIHVua25vd24sIGEgcHJhY3RpY2FsIGFwcHJvYWNoIGlzIHRvIGJlZ2luIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24gYXMgYSBiYXNlbGluZSBiZWZvcmUgY29uc2lkZXJpbmcgTUxQcyBpZiByZXNpZHVhbHMgc3VnZ2VzdCB1bm1vZGVsZWQgbm9ubGluZWFyaXR5Lg0KDQpcDQoNCioqU2luZ2xlLUhpZGRlbi1MYXllciB2cy4gVHdvLUhpZGRlbi1MYXllciBNTFBzKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiAqKnNpbmdsZS0qKiBhbmQgKip0d28taGlkZGVuLWxheWVyIE1MUHMqKiB1bmRlcnNjb3JlcyBtb2RlbCBjb21wbGV4aXR5LiANCg0KKiAqKkRlZXBlciBuZXR3b3JrcyoqIHRoZW9yZXRpY2FsbHkgaGF2ZSBncmVhdGVyIHJlcHJlc2VudGF0aW9uYWwgcG93ZXIuIENhc2Ugc3R1ZGllcyBmb3VuZCB0aGF0IHRoZSAqKnR3by1oaWRkZW4tbGF5ZXIqKiBNTFAgZGlkIG5vdCBvdXRwZXJmb3JtIGl0cyBzaW1wbGVyIGNvdW50ZXJwYXJ0LCBkZXNwaXRlIGhhdmluZyBtb3JlIHBhcmFtZXRlcnMuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHRoZSB1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtLCB3aGljaCBzdGF0ZXMgdGhhdCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgKHdpdGggc3VmZmljaWVudCBuZXVyb25zKSBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24uIA0KDQoqIFRoZSBhZGRpdGlvbmFsIGxheWVyIGludHJvZHVjZWQgdW5uZWNlc3NhcnkgY29tcGxleGl0eSB3aXRob3V0IGdhaW5zIGluIGFjY3VyYWN5LCByZWluZm9yY2luZyB0aGF0IGRlZXBlciBhcmNoaXRlY3R1cmVzIHNob3VsZCBvbmx5IGJlIGV4cGxvcmVkIHdoZW4gc2ltcGxlciBtb2RlbHMgcHJvdmUgaW5hZGVxdWF0ZeKAlHR5cGljYWxseSBpbiBoaWdobHkgbm9ubGluZWFyIG9yIGhpZ2gtZGltZW5zaW9uYWwgcHJvYmxlbXMuDQoNClwNCg0KKipHZW5lcmFsIFJlY29tbWVuZGF0aW9ucyoqDQoNCiAgKyAqKlN0YXJ0IHNpbXBsZSoqOiBBbHdheXMgYmVnaW4gd2l0aCBjbGFzc2ljYWwgbW9kZWxzIChsaW5lYXIvbG9naXN0aWMgcmVncmVzc2lvbikgYXMgYmFzZWxpbmVzLiBUaGVpciBpbnRlcnByZXRhYmlsaXR5IGFuZCBlZmZpY2llbmN5IG1ha2UgdGhlbSBpZGVhbCBmb3IgaW5pdGlhbCBleHBsb3JhdGlvbi4NCg0KICArICoqVXNlIE1MUHMganVkaWNpb3VzbHkqKjogUmVzZXJ2ZSBNTFBzIGZvciBjYXNlcyB3aGVyZSBsaW5lYXIgbW9kZWxzIHVuZGVycGVyZm9ybSBkdWUgdG8gY2xlYXIgbm9ubGluZWFyaXR5IG9yIGludGVyYWN0aW9uIGVmZmVjdHMuIFByZWZlciBzaW5nbGUtaGlkZGVuLWxheWVyIGFyY2hpdGVjdHVyZXMgdW5sZXNzIGRlZXBlciBuZXR3b3JrcyBkZW1vbnN0cmFibHkgaW1wcm92ZSBwZXJmb3JtYW5jZS4NCg0KICArICoqQmFsYW5jZSBjb21wbGV4aXR5IGFuZCBwZXJmb3JtYW5jZSoqOiBBdm9pZCB1bm5lY2Vzc2FyeSBtb2RlbCBjb21wbGV4aXR54oCUZmF2b3IgdGhlIHNpbXBsZXN0IG1vZGVsIHRoYXQgYWNoaWV2ZXMgc2F0aXNmYWN0b3J5IGFjY3VyYWN5Lg0KDQogICsgKipIeWJyaWQgYXBwcm9hY2gqKjogQ29tYmluZSBsaW5lYXIgbW9kZWxzIHdpdGggZmVhdHVyZSBlbmdpbmVlcmluZyBvciBrZXJuZWwgbWV0aG9kcyBiZWZvcmUgcmVzb3J0aW5nIHRvIE1MUHMsIHBhcnRpY3VsYXJseSBpbiByZXNvdXJjZS1jb25zdHJhaW5lZCBzZXR0aW5ncy4NCg0KDQoNClVsdGltYXRlbHksIG1vZGVsIGNob2ljZSBzaG91bGQgYmUgZ3VpZGVkIGJ5IHByb2JsZW0gY29uc3RyYWludHMsIGRhdGEgc3RydWN0dXJlLCBhbmQgdGhlIHRyYWRlLW9mZnMgYmV0d2VlbiBhY2N1cmFjeSwgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGNvbXB1dGF0aW9uYWwgY29zdC4gQSBzeXN0ZW1hdGljLCBpdGVyYXRpdmUgYXBwcm9hY2jigJRmcm9tIGxpbmVhciBtb2RlbHMgdG8gc2hhbGxvdyB0aGVuIGRlZXAgbmV0d29ya3PigJRlbnN1cmVzIGJvdGggZWZmaWNpZW5jeSBhbmQgcGVyZm9ybWFuY2UuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=